问题引入
有N堆石子排成一排,每一堆石子有一定的数量。现在将N堆石子合并成为一堆。合并的过程中只能每次将N堆石子合并成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求总的代价最小值。
题目特点分析:
1、求某个区间的最优解
2、大的区间由包含于他的小区间组成
3、满足DP的三个基本条件
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);
for(int len=2;len<=n;len++) // 区间长度
for(int i=1;i<=n;i++) // 枚举起点
{
int j =i+len-1; // 计算出区间终点
if(j>n) break; // 越界结束
for(int k=i;k<j;k++) // 枚举分割点,构造状态转移方程
{
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);
}
}
// 特别的,初始化时,当i=j,由于不需要合并,所以dp[i][j]=0
//记忆化DFS实现方法,更简单,不容易出错
int MINdfs(int l,int r) //记忆化DFS 初始化为-1
{
int &D = dp[l][r];
if(D!=inf) return D;
if(l==r) return D=0;
for(int i=l;i<r;i++)
D = min(D,MINdfs(l,i)+MINdfs(i+1,r)+sum[r]-sum[l-1]);
return D;
习题练习
1、 YOU ARE THE ONE
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int N = 105;
const int inf = 0x3f3f3f3f;
int v[N],sum[N],f[N][N];
int T,n;
int dp(int i,int j)
{
if(i>=j) return 0;
if(f[i][j]!=inf) return f[i][j];
for(int k=1;k<=j-i+1;k++)
f[i][j]=min(f[i][j],dp(i+1,i+k-1)+dp(i+k,j)+k*(sum[j]-sum[i+k-1])+v[i]*(k-1));
return f[i][j];
}
int main()
{
cin >> T;
for(int c=1;c<=T;c++)
{
cin >> n;
for(int i=1;i<=n;i++)
{
cin >> v[i];
sum[i]=sum[i-1]+v[i];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
f[i][j]=inf;
}
cout << "Case #" << c << ": "<<dp(1,n)<< endl;
}
return 0;
}
2、String painter
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int N = 105;
const int inf = 0x3f3f3f3f;
int v[N],f[N],dp[N][N];
int T,n;
int main()
{
char s1[N],s2[N];
while(~scanf("%s%s",s1,s2))
{
memset(dp,0,sizeof(dp));
int n = strlen(s1);
for(int i=0;i<n;i++)
dp[i][i]=1;
for(int l=1;l<n;l++)
{
for(int i=0;i+l<n;i++)
{
int j = i+l;
dp[i][j]=dp[i+1][j]+1;
for(int k=i+1;k<=j;k++)
dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]+(s2[i]!=s2[k]));
}
}
for(int i=0;i<n;i++){
f[i]=dp[0][i];
if(s1[i]==s2[i])
{
if(i==0) f[i]=0;
else f[i]=f[i-1];
}
for(int k=0;k<i;k++)
f[i]=min(f[i],f[k]+dp[k+1][i]);
}
cout << f[n-1] << endl;
}
}