本周主要看了线性dp和背包问题。
线性dp题目做出来的关键基本上是推出状态转移方程,只要推出来了,这道题基本就做完了。但这不太容易,就本周看的博客题目来说,很多题看一眼就是不会,有些题看了题解都不知道为什么这么做,直到周末还是有些题想不明白。看的dp博客越多越觉得自己跟他们的差距好大。下边是部分我想明白了的有点特殊线性dp题
P1006 [NOIP2008 提高组] 传纸条 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P1004 [NOIP2000 提高组] 方格取数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这两道题做法基本上是一样的,学到了2种做法,一种是4维,另一种是3维。2维的做法还没有想明白。
4维做法:两人的纸条从同一起点出发,设dp[m1][n1][m2][n2]为第一个人的纸条传到(m1,n1),第二个人的纸条传到(m2,n2)时的最大好心程度和。两张纸条的上一次位置可能是(m1-1,n1)与(m2-1,n2),(m1-1,n1)与(m2,n2-1),(m1,n1-1)与(m2-1,n2),(m1,n1-1)与(m2,n2-1)。
关键代码:
for(int m1=0;m1<m;m1++)
for(int n1=0;n1<n;n1++)
for(int m2=0;m2<m;m2++)
for(int n2=0;n2<n;n2++)
dp[m1][n1][m2][n2]=max(dp[m1-1][n1][m2-1][n2],dp[m1-1][n1][m2][n2-1],dp[m1][n1-1][m2-1][n2],dp[m1][n1-1][m2][n2-1])+a[m1][n1]+a[m2][n2];
3维做法:因为两个纸条的步数是一样的也就是(m1+n1)==(m2+n2),也就是只要再知道两个纸条的横坐标或纵坐标,就可以确定这两个点的坐标,另一个坐标为步数减去知道的横或纵坐标。设dp[i][x1][x2]为第m步时第一个人的纸条传到(x1,i-x1+1)和第二个人的纸条传到(x2,i-x2+1)时的最大好心程度和。在之前还要判断是否越界,在之后还得进行判重。两张纸条上一次的位置与4维差不多。关键代码:
for(int i=1;i<=m+n;i++)
for(int x1=1;x1<=m;x1++)
for(int x2=1;x2<=m;x2++)
{if(i<x1||i<x2)continue;
dp[i][x1][x2]=max(dp[i-1][x1][x2],dp[i-1][x1-1][x2-1],dp[i-1][x1-1][x2],dp[i-1][x1][x2-1])+a[x1][i-x1+1]+a[x2][i-x2+1];
if(x1==x2)dp[i][x1][x2]-=a[x1][i-x1+1];
}
这道题与普通背包问题有一些不同,普通背包数组下标为剩余空间且数组表示最大价值,这个下标为能抢到的钱且数组表示最大逃脱概率。令dp[j]为抢j元的最大逃脱概率,方程为dp[j]=max(dp[j],dp[j-a[i]]*(1-b[i]))。dp完后还需要在抢钱数从大到小循环,找到的第一个偷盗者能接受的概率所对应的钱数就是最大抢钱数。
这道题还得注意初始化问题,令dp[0]=1,,因为没有抢钱逃脱概率为1.
关键代码:
for(int i=1;i<=n;i++){
for(int j=sum;j>=a[i];j--){
dp[j]=max(dp[j],dp[j-a[i]]*(1-b[i]));}
CodeForces 687C - The Values You Can Make(01背包dp)_TianTengtt的博客-CSDN博客
这题的做法,我之前根本没想到还可以用bool型的二维数组来dp。
就目前看到这些线性dp(不是很难的)的题绝大多数都是使数组下标m表示以m为结尾的最大的数。而那些难的题是用一些想不到(有些还不理解)的思路去解决的,中间的几篇博客关于线性dp的题目是真的难啊,基本上都是想半天还不知道为什么这么做。水平还是不够,而且后面还有很多种dp的题目,都不是很简单,相比较来说背包问题(01背包,完全背包,多重背包)貌似是简单的?之后还是要多看一些题目的不同人写的题解,这可能会对我想明白思路有点帮助。