【NOI 1995】石子合并

一道简单但是仍不会做的题。我真的是一个脑残吗

其实,我暗戳戳地觉得这是贪心。猜想不能用贪心的原因是必须用连续的交换吧。(合并可能改变位置)

好吧,其实我就是因为这是道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啊。

好了,我要溜了,拜拜!(如有问题请在评论区留言)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值