今天袁老师给我们讲了几个经典DP模型
如下:
1.合并石子
题意:有n堆石子,两堆相邻的石子可以堆在一起,最终要使得这n堆石子合并成一堆,新堆的石头数记为本次合并的得分,求最小的得分
袁老师首先用递推给我们分析了方案总数的求和→Catalan数
然后告诉了我们递推=统计 动规=选择 的区别
开始分析动规做法
首先,
输入每堆石子数,初始化:s[i]=s[i-1]+本次石子数
i from 1 to n
将f[i][i]初始化为0,(因为一开始只知道f[i][i]的初值)
倒着来,把一堆石子每次分成两堆,k的位置就是分界点
s[i]表示前i堆石头的数量总和,f[i][j]表示把第i堆石头到第j堆的石头合并成一堆的最优值(这里最优后面也会最优,因此满足最优子结构性质)
然后,核心代码:
for(int i=n-1;i>=1;i++) for(int j=i+1;j<=n;j++) for(k=i;j<=j-1;k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]) 输出f[1][n]
f[i][j]=min{f[i][k]+f[k+1][j]}+sum[j-1]+sum[i-1](从i到j石子量)
for(i=1~n-1)
for(j=i+1~n)
for(k=i~j-1)
i为左端点
j为右端点
2.乘积最大
题意:给定两个整数n和m,表示一个长度为n的数字串,你可以在其中插入m个乘号
一开始袁老师“随便”举出了一个数字“365940857”,让我们知道了老师的qq号
咳咳咳,我什么都没说,老师又拿了一个乘号告诉我们当把这个数字劈开两半时,有一半还有一次劈的机会,有两种可能:1.左边有机会,右边无 2.右边有机会,左边无
总而言之用k来确定劈的位置,使得两边最优,再选择最优边再劈,以此类推(这是区间dp)
正解如下:
初始化:f[i][0]=前i个数本身
j from 1 to m
i from j+1 to n
k from j to i-1
f[i][j]表示前i个数插j个乘号,在k和k+1中间加乘号,则有f[i][j]=max(f[i][j],f[k][j-1]*a[k+1][i])
输出f[n][m]
3.机器分配
i个公司分配j台机器
f[i][j]=f[i-1][j-k](分配了k台,前i-1个公司还剩j-k台)+a[i][k]
k不超过j
4.挖地雷
用邻接矩阵保存每个地窖到另一个地窖,二维数组[i][j]表示从i到j是否有通路,有是1,没有是0
枚举每一个其他地窖到这个地窖的路径,能走就加上地雷数

1 #include <bits/stdc++.h> 2 int n,maxn=0; 3 int main(){ 4 int bomb[20+5],road[20+5][20+5],f[20+5],c[20+5]; 5 memset(road,0,sizeof(road)); 6 memset(f,0,sizeof(f)); 7 memset(c,0,sizeof(c)); 8 scanf("%d",&n); 9 for(int i=1;i<=n;i++){ 10 scanf("%d",&bomb[i]); 11 } 12 for(int i=1;i<=n-1;i++){ 13 for(int j=i+1;j<=n;j++){ 14 scanf("%d",&road[i][j]); 15 } 16 } 17 f[n]=bomb[n]; 18 int l=0,k=0; 19 for(int i=n-1;i>=1;i--){ 20 l=0; 21 k=0; 22 for(int j=i+1;j<=n;j++){ 23 if(road[i][j]==1&&f[j]>l){ 24 l=f[j]; 25 k=j; 26 } 27 } 28 f[i]=l+bomb[i]; 29 c[i]=k; 30 } 31 k=1; 32 for(int i=2;i<=n;i++){ 33 if(f[i]>f[k]){ 34 k=i; 35 } 36 } 37 maxn=f[k]; 38 printf("%d",k); 39 k=c[k]; 40 while(k!=0){ 41 printf(" %d",k); 42 k=c[k]; 43 } 44 printf(" %d",maxn); 45 return 0; 46 }
5.友好城市
从1~n将北岸城市一一编号,一一对应将南岸城市编号,两岸对应连线,求两岸城市编号最长公共子序列
6.橱窗布置
有f束花,v个花瓶
初始化:
1.我们知道f[0][0]一定是0
2.f[1][j]=a[1][j](a数组表示前i束花插到前j个花瓶里的美学价值)(j from 1 到 v-f+1)
所以只需让i从2到f
j从i到v-f+i循环
f[i][j]表示第i束花插到第j个花瓶里
f[i][j]=f[i-1][k]+a[i][j](考虑最后一束花插到第k个花瓶里,因为不知道k是多少,所以循环k=i-1 to j-1)
7.方格取数
不管怎样,都走n+m-2步
假设两个人走,取两人走的最大值
f[i][j][k][l]表示第一个人在i行j列,第二个人在k行l列
i,j,k,l均从1 to n
f[i][j][k][l]=max(f[i-1][j][k-1][l],f[i][j-1][k-1][l],f[i-1][j][k][l-1],f[i-1][j][k-1][l])(不能越界)
取最大值加a[i][j]
if(i!=k&&j!=l)
f[i][j][k][l]+=a[k][l]
输出f[n][n][n][n]
需思考:机器分配
That‘s all