寒假集训Day3

今天袁老师给我们讲了几个经典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("\n%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
posted @ 2018-02-03 14:04  Friction_alan  阅读(154)  评论(5编辑  收藏  举报



















































Contact with me