多维dp可以使用坐标来表示状态,P1004,就可以将每个方格的f值开为f[i][j][k][l],将两个点分别在(i,j)与(k,l)的状态表示,进行状态转移,对于特定的情况,可以进行降维,P1006,由于传纸条是只能向下和向右传递,所以到达每个点的步数是一定的,为i+j-1(初始点不同表示方法不同,也可以是i+j-2),这样就可以开一个三维数组,f[k][i][j],用k来表示到达这个状态的步数,i,j表示该状态两个点的行(列)数,由于步数与行列数的关系,可以算出该点的行列坐标,进行与四维相同的转移即可,另外一点就是,在这两个题中是要求不能到达同一点,但要求的其实是总和,所以不管哪一点进入该状态都可以,判断(i==j)f-=该点的val;
p1006转移代码
1 for(int k=2;k<=n+m-1;k++) 2 for(int i=1;i<=n&&i<=k;i++) 3 for(int j=1;j<=n&&j<=k;j++) 4 { 5 f[k][i][j]=max(max(max(f[k-1][i-1][j-1],f[k-1][i][j-1]),f[k-1][i-1][j]),f[k-1][i][j])+val[k-i+1][i]+val[k-j+1][j]; 6 if(i==j)f[k][i][j]-=val[k-j+1][j]; 7 } 8 cout<<f[n+m-1][n][n];
区间DP【我是谁,我在那?
对于区间的状态转移个人认为最主要的是最小单元和合并方式,以及状态表示,对于一个区间的操作,如何才能借此推出下一个区间就是一个关键性问题,由于是区间的操作,有可能会出现向左与向右的问题,所以可能会需要加入一个维度来表示区间的左与右,P1220,就是一个典型的在区间上表示左右的题;
对于大多数的区间DP,记忆化搜索与递推都是可以转换的,记忆化搜索是去除,而递推则是加入,比如P1005,就是一个两种方法均可。对与递推,还有注意一部分的倒序,这样才能保证无后效性,
区间DP的转换性也是很强的,当一个矩阵行与行之间没有关联时,就可以转变为每一行的区间DP,矩形取数就是这样的一个题。
P1220
1 f[s][s][0]=0;f[s][s][1]=0; 2 for(int i=s;i>=1;i--)for(int j=i+1;j<=n;j++) 3 { 4 f[i][j][0]=min(f[i+1][j][0]+(a[i+1]-a[i])*t[i+1][j] 5 ,f[i+1][j][1]+(a[j]-a[i])*t[i+1][j]); 6 f[i][j][1]=min(f[i][j-1][0]+(a[j]-a[i])*t[i][j-1] 7 ,f[i][j-1][1]+(a[j]-a[j-1])*t[i][j-1]); 8 } 9 cout<<min(f[1][n][1],f[1][n][0]);
P1005
1 a[0]=1;f(i,m)a[i]=a[i-1]*2; 2 f(i,n) 3 { 4 for(int r=1;r<=m;r++) 5 for(int l=r;l>=1;l--) 6 { k=m-(r-l); 7 f[l][r]=max(f[l+1][r]+num[i][l]*a[k],f[l][r-1]+num[i][r]*a[k]); 8 } 9 ans+=f[1][m]; 10 f(k,m)f(o,m)f[k][o]=0; 11 }
做了一点树形DP的题,觉得树形DP感觉很少会出现其他类型,【其实是太弱了
一类是类似没有上司的舞会,和最大独立集有关的题,另外一类就是树上的分组背包,通过枚举分给每一个子树的权值,感觉很套化,多练一练掌握一下
而且树上背包个人感觉记忆化搜索更优,比较舒服
P1270,
void init(int cnt) { int a; cin>>a>>val[cnt]; tme[cnt]=a*2; if(!val[cnt]) { init(cnt>>1); init(cnt>>1|1); } } int dfs(int cnt,int tot) {if(f[cnt][tot]>0||!tot)return f[cnt][tot]; if(val[cnt])return f[cnt][tot]=min(val[cnt],(tot-tme[cnt])/5); for(int i=0;i<=tot-tme[cnt];i++) f[cnt][tot]=max(f[cnt][tot],dfs(cnt<<1,i)+dfs(cnt<<1|1,tot-tme[cnt]-i)); return f[cnt][tot]; } int main() { cin>>t; init(1); cout<<dfs(1,t-1); }
论如何枚举所有状态————P1077
本来以为做那么多题能写出来结果还是失败了
就差那么一点啊
再表示一下数量啊喂
int main() { cin>>n>>m; for(int i=0;i<=n;i++)f[i][0]=1; for(int i=1;i<=n;i++) { cin>>a; for(int j=0;j<=a;j++) for(int k=0;k<=m-j;k++) { if(!j&&!k)continue; f[i][j+k]=(f[i-1][k]+f[i][j+k])%mods; } } cout<<f[n][m]%mods; }