CodeForces1312E 区间DP
题意
伊珂丝是个爱玩游戏的少女。经常因为贪玩而耽误了咖啡馆的工作,也因此常被店长吐槽。不过真到了干活的时候,伊珂丝也是当仁不让的!
这次伊珂丝又来找店长PK了,如果店长赢下这局,伊珂丝就答应在咖啡馆里干一天活儿,你能帮帮店长,战胜伊珂丝吗?
游戏规则如下:
游戏中一开始有n个数字排成一排,这n(<=500)个数的大小都在1~1000之间。
每次可以选择两个 相邻且相等 的数字,将它们合并成一个数字。
合并后的数字是原来的数字 +1,合并后数字个数 -1。
如果没有满足合并条件的数,则游戏结束。最后剩下的数字越少越好。
现在给出n与这些数字的信息,你能帮助店长算一算,最好成绩是剩下几个数字?
解法
看数据范围和题意不难想到是区间DP,唯一值得讨论的是dp数组的维护形式,这里给出的形式是用一个node表示dp的节点,包含num(区间内元素数量), l(区间左值), r(区间右值),以num越小的node越优先来更新dp
tips:
- 看似这样的表示方法不能唯一表示一个节点,形如 2 2 2的区间既可以表示为num = 2, l = 2, r = 3,也可以表示为num = 2, l = 3, r = 2,但是对于这道题来说不关键
- 对于样例3 2 2 2而言,将由[1,3] + [4,4]来更新到全局,对于2 2 2 3而言,将有[1,1] + [2,4]来更新到全局,实际上总有一种区间合并的方式来更新到num最小的全局答案,与区间[2,2,2]计算的l,r是否唯一无关
代码
#include<bits/stdc++.h>
using namespace std;
#define PII pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define LL long long
const int maxn = 500 + 10;
const int mod = 1e9 + 7;
int N,M,S;
int a[maxn];
struct node {
int l,r,num;
};
node dp[maxn][maxn];
int main(){
int N; scanf("%d",&N);
for(int i = 1; i <= N ; i ++) {
for(int j = 1; j <= N; j ++) {
dp[i][j].num = 0x3f3f3f3f;
}
}
for(int i = 1; i <= N ; i ++) {
scanf("%d", &a[i]);
dp[i][i].num = 1;
dp[i][i].l = dp[i][i].r = a[i];
}
for(int len = 1; len <= N; len ++) {
for(int l = 1; l + len - 1 <= N; l ++) {
int r = l + len - 1;
for(int k = l ; k <= r - 1; k ++) {
int num = dp[l][k].num + dp[k + 1][r].num;
int L = dp[l][k].l,R = dp[k + 1][r].r;
if(dp[l][k].r == dp[k + 1][r].l) {
num--;
if(dp[l][k].num == 1) L++;;
if(dp[k + 1][r].num == 1) R++;
}
if(num < dp[l][r].num) {
dp[l][r].num = num;
dp[l][r].l = L;
dp[l][r].r = R;
}
}
}
}
printf("%d",dp[1][N].num);
return 0;
}
// 5 4 3 2 1 1
// 9 4 3 2 1 1 2 3 4 5