洛谷 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\n", g[n]);
	return 0;
}

End

posted @ 2021-10-03 08:11  xixike  阅读(33)  评论(0编辑  收藏  举报