区间DP
区间DP
一.石子合并1
题目描述:
有N堆石子排成一排(n<=100),现要将石子有次序地合并成一堆,规定每次只能选相邻的两堆合并成一堆,并将新的一堆的石子数,记为改次合并的得分,编一程序,由文件读入堆数n及每堆石子数(<=200);
(1)选择一种合并石子的方案,使得做n-1次合并,得分的总和最少
(2)选择一种合并石子的方案,使得做n-1次合并,得分的总和最多
输入格式:
第一行为石子堆数n 第二行为每堆石子数,每两个数之间用一空格分隔。
输出格式:
从第1行为得分最小第二行是得分最大。
该问题大致有两种,一种是链状的区间DP,另一种是环状的区间DP,处理环状的问题时,直接把链接在他自己后面即可,此处只展示链状问题的解决。
该题f[i][j]表示从第i个到第j个石子合并的最大值,用j选择断开位置以转化为子问题的最优解,这也是决策的部分。
那么就可以列出状态转移方程: f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+cost)
最大值同理:f[i][j]=max(f[i][k]+f[k+1][j]+cost)
完整代码如下:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=300;
int v[N],w[N],f[N][N],s[N],g[N][N];
int n,m,i,k;
int main(){
cin>>n;
for(i=1;i<=n;i++){
scanf("%d",&s[i]);
s[i]+=s[i-1];
}
memset(f,0x7f,sizeof(f));
for(i=1;i<=n;i++)f[i][i]=0;
for(int len=2;len<=n;len++){
for(i=1;i+len-1<=n;i++){
int j=i+len-1;
for(k=i;k<j;k++){
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
g[i][j]=max(g[i][j],g[i][k]+g[k+1][j]+s[j]-s[i-1]);
}
}
}
cout<<f[1][n]<<endl<<g[1][n];
return 0;
}