洛谷 P5154 数列游戏

Description

洛谷传送门

Solution

明显的区间dp

根据套路,设计状态:

(f[i][j]) 表示,删去区间 ([i, j]) 之间的数,能得到的最大得分。

但是我们发现,这个并不好转移,我们无法记录是否都能被删除。

所以我们转换一下思路。

(f[i][j]) 表示,区间 ([i, j]) 能否被完全删除。

那么我们有两种可能:

  1. (a[i])(a[j]) 不互质,那么我们可以从 (f[i + 1][j - 1]) 转移过来。

  2. 枚举断点 (k),我们先令区间 ([i, k]) 和 区间 ([k + 1, j]) 合并,在合并区间 ([i, j])

有了判断能否删除的状态之后,我们还要统计答案。

所以设 (g[i]) 为处理前 (i) 个数之后能得到的最大得分。

那么转移方程就是:(g[i] = max(g[i], g[j - 1] + sum[i] - sum[j - 1]))

(g[i]) 初值为 (g[i - 1])(sum[i]) 为前 (i) 个数的分数前缀和。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#define ll long long

using namespace std;

const ll N = 1010;
ll n;
ll a[N], b[N], f[N][N], g[N];
ll sum[N];

inline ll gcd(ll a, ll b){
	return !b ? a : gcd(b, a % b);
}

signed main(){
	scanf("%lld", &n);
	for(ll i = 1; i <= n; i++)
		scanf("%lld", &a[i]);
	for(ll i = 1; i <= n; i++){
		scanf("%lld", &b[i]);
		sum[i] = sum[i - 1] + b[i];
	}
	for(ll i = 1; i < n; i++)
		f[i][i + 1] = (gcd(a[i], a[i + 1]) != 1);//f 数组初值
	for(ll len = 3; len <= n; len++)
		for(ll i = 1; i + len - 1 <= n; i++){
			ll j = i + len - 1;
			if(len > 3 && gcd(a[i], a[j]) != 1) f[i][j] |= f[i + 1][j - 1];//转移 1
			for(ll k = i + 1; k < j - 1; k++)
				f[i][j] |= (f[i][k] & f[k + 1][j]);//转移 2
		}
	for(ll i = 1; i <= n; i++){
		g[i] = g[i - 1];
		for(ll j = 1; j <= i; j++)
			if(f[j][i])
				g[i] = max(g[i], g[j - 1] + sum[i] - sum[j - 1]);
	}
	printf("%lld
", g[n]);
	return 0;
}

End

本文来自博客园,作者:xixike,转载请注明原文链接:https://www.cnblogs.com/xixike/p/15363382.html

原文地址:https://www.cnblogs.com/xixike/p/15363382.html