很多dp的模型是十分重要的,需要记住
首先是石子合并这个模型,它有很多种变式,
比如把合并的代价是两堆石子的数量改成分离的代价是这堆石子的总数(bzoj 分卷子)
又如把合并点改成合并边(uva10003切木棍),还有把石子变成一个环的啊(hdu3506Monkey Party),都是如此
dp[i][j]表示合并第i堆到第j堆石子的最小代价
石子合并的状态转移方程: dp[i][j]=min(dp[i][k]+dp[k+1][j]+cost[i][j])
石子合并模型代码
for(int l=2;l<=n;l++)
for(int i=1;i<=n-l+1;i++)
{
int j=i+l-1;
dp[i][j]=MAXM;
for(int k=i;k<j;k++)
if(dp[i][j]>dp[i][k]+dp[k+1][j]+cost[i][j])
dp[i][j]=dp[i][k]+dp[k+1][j]+cost[i][j];
}
把合并和分离是完全等价的,需要懂得转换
环形的石子合并只需要将数组长度*2-1模拟环,其余与普通的石子合并相同
至于把点看成边的石子合并嘛,方程式和代码都有一点小小的不同
状态转移方程:dp[i][j]=min(dp[i][k]+dp[k][j]+cost[i][j])
代码:
for(int l=1;l<=n;l++)
for(int i=0;i<=n-l;i++)
{
int j=i+l;
if(l==1) dp[i][j]=0;
else if(l==2) dp[i][j]=cost[i][j];
else
{
dp[i][j]=inf;
for(int k=i+1;k<j;k++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+cost[i][j]);
}
}
石子合并暂且就说这么多
然后是最长上升子序列
事实上,这个模型的变式并不像石子合并这么多,而是这个dp的思想非常普遍和重要
状态转移方程:dp[i]=max(dp[j]+1) (i>j&&h[i]>h[j])
有时,题目的方程并不是这个,但是需要用到这种思想(uva437 巴比伦塔)
最长下降不上升不下降不再赘述,有时间还会补上最长公共子序列的模型
背包问题:
这些背包问题变式之多,令人发指,这里只讨论01背包,多重背包和完全背包,还想要更多可以去看这个网址:
http:
//www.cppblog.com/tanky-woo/archive/2010/07/31/121803.html
背包问题是个什么东西不用多解释,01背包就是每种物品只能选一个,多重背包是每种限n[i]个,完全背包不限
01背包:
dp[i][j]表示将前i件物品放入容量为v的背包中可以获得的最大价值
状态转移方程:dp[i][v]=max(dp[i-1][v],dp[i-1][v-c[i]]+w[i])
可以空间优化为1维:dp[v]=max(dp[v],dp[v-c[i]]+w[i]) 需逆序进行访问
完全背包:
dp定义同01背包
dp[i][v]=max(dp[i-1][v-k*c[i]]+k*w[i]) (0<=k*c[i]<=v)
同样可以优化为1维,需顺序访问
多重背包:
除了不能空间优化和0<=k<=n[i]之外,与完全背包无异
后面有点水,以后会修改。。