本周主要是看完了背包问题,自学了一点区间DP问题。
就目前看的博客,对于简单题,背包问题(01背包、完全背包、多重背包和)就是各自的套路,难得就是虽难,但比其他的DP简单。下面是几个自认为特别的题:
P1064 [NOIP2006 提高组] 金明的预算方案 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这个题是01背包得变种,与普通的01背包区别是这个题的决策有不选、选主件、选主件和一个附件、选主件和另一个附件、选主件和两个附件共五种。只要想明白这个,这个题就很简单了。关键代码:
for(int i=1;i<=m;i++)
for(int i1=n;i1>=0;i1--)
{
if (i1>=a1[i])
f[i1] = max(f[i1], f[i1-a1[i]]+b1[i]);
if (i1>=a1[i]+a2[i][2])
f[i1] = max(f[i1], f[i1-a1[i]-a2[i][2]]+b1[i]+b2[i][2]);
if (i1>=a1[i]+a2[i][1])
f[i1] = max(f[i1], f[i1-a1[i]-a2[i][1]]+b1[i]+b2[i][1]);
if (i1>=a1[i]+a2[i][1]+a2[i][2])
f[i1] = max(f[i1], f[i1-a1[i]-a2[i][1]-a2[i][2]]+b1[i]+b2[i][1]+b2[i][2]);
}
P1091 [NOIP2004 提高组] 合唱队形 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这个题一开始根本不知道怎么做,看了题解才明白,这种思维题真是不想明白就很难,想明白就变简单了。思路就是:正着求一遍最长上升子序列,再倒着求一遍最长上子序列,最后枚举相加求最大的那个,即:
for(int i=1;i<=n;i++) ans=max(f[0][i]+f[1][i]-1,ans);
最后加一是因为左右都加了自己,自己一共加了两次,所以要减去一个。
P1387 最大正方形 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这道题也是不看答案不知道怎么做。状态转移方程:
if(a[i][j]==1)f[i][j]=min(min(f[i][j-1],f[i-1][j]),f[i-1][j-1])+1;
f[i][j]表示以(i,j)为右下角时,可构成的最大的正方形的边长。至于为什么这个转移方程是对的,我是看了几个别人举出来的例子和解释,就很明白了。对于这种与图形有关的题,也许以后在做不出来时需要在纸上画画图,这样可以更好的思考题。
这个题是个特殊的01背包,获得的价值与购买顺序有关。只要想明白这个,这题就很简单。做法只需要在01背包前根据Qi-Pi的差值从小到大排序。关键代码:
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
for(int j=m;j>=a[i].q;j--)
{dp[j]=max(dp[j],dp[j-a[i].p]+a[i].v);}
Proud Merchants详细解答 - MrMission - 博客园 (cnblogs.com) 这个讲的更详细。
真的是看得越多越觉得自己与他们差距很大,但之后也只有多看才能有所提高。