一道简单但是仍不会做的题。我真的是一个脑残吗
其实,我暗戳戳地觉得这是贪心。猜想不能用贪心的原因是必须用连续的交换吧。(合并可能改变位置)
好吧,其实我就是因为这是道dp才做的。
由于是环,我们就要破环为链。(方便操作)
首先,我们肯定不能用i从i-1那里推的dp来干。我们发现,当合并一个大坨时,可以选择大坨包含的左右两小坨来合并,递推式很好给:
d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k + 1 ] [ j ] + w [ i ] [ j ] dp[i][j]=dp[i][k]+dp[k+1][j]+w[i][j] dp[i][j]=dp[i][k]+dp[k+1][j]+w[i][j]
其中w数组也很好算,就是i到j的数字和。
上马!
#include<cstdio>
#include<iostream>
using namespace std;
const int N = 402, limit = (1 << 30) - 1;
int n, point[N], dp[N][N][3], w[N][N], ans1 = limit, ans2;
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
scanf("%d", &point[i]);
point[i + n] = point[i];
}
for(int i = 1; i < n * 2; i ++)
for(int j = i; j < n * 2; j ++)
w[i][j] = w[i][j - 1] + point[j], dp[i][j][0] = limit;
for(int i = 1; i < n * 2; i ++)
dp[i][i][0] = 0;
for(int len = 1; len < n; len ++) {
for(int i = 1, j = i + len; j < n * 2; i ++, j ++) {
for(int k = i; k <= j; k ++) {
dp[i][j][0] = min(dp[i][j][0], dp[i][k][0] + dp[k + 1][j][0] + w[i][j]);
dp[i][j][1] = max(dp[i][j][1], dp[i][k][1] + dp[k + 1][j][1] + w[i][j]);
}
}
}
for(int i = 1; i <= n; i ++) {
ans1 = min(ans1, dp[i][i + n - 1][0]);
ans2 = max(ans2, dp[i][i + n - 1][1]);
}
printf("%d\n%d\n", ans1, ans2);
return 0;
}
你准备策码奔腾了吗?其实这份代码有问题。你会发现,max值会比原值大2倍,除以2提交,它A了
。。。其实,k必须严格小于j。后面越界倒是无所谓,你发现dp[i][k][1]此时就是dp[i][j]!而我们知道,这样可推得dp[i][i+1]的值乘了2。而后每次累加原来应得的乘以2,再加上自己加了2次,最后算出来的答案自然就乘了2。至于为什么min操作未受影响,是因为别人取min啊。
好了,我要溜了,拜拜!(如有问题请在评论区留言)