洛谷 P1174 打砖块
虽然打到标记为Y的砖块上相当于没有消耗子弹,但是首先需要从别的列“借”一发子弹。因为如果没有子弹打在上面,我们无法获得奖励的子弹和分数。
所谓借一发子弹,其实是打子弹的先后顺序,比如第2列要借一发子弹给第1列,那么把将要打第2列的一发子弹先用来打第1列,然后第1列获得奖励的子弹后,再把那一发子弹还给第二列。
dp[i][j][0]表示前i列用了j发子弹且最后一发打在标记为N的砖块上的最大分数,dp[i][j][1]表示前i列用了j发子弹且最后一发打在标记为Y的砖块上的最大分数。
首先预处理把所有标记为Y的砖块压到它下面标记为N的砖块上,用s[i][j][0]表示第i列用了j发子弹且最后一发打在标记为N的砖块上获得的分数,s[i][j][1]表示第i列用了j发子弹且最后一发打在标记为Y的砖块上获得的分数。
最后的答案一定是打在标记为N的砖块上,即dp[m][K][0],如果打在Y的砖块上,还可以继续再打,一定不是最优的。
#include<bits/stdc++.h> using namespace std; int n,m,K; int w[210][210],dp[210][210][2]; int s[210][210][2]; bool op[210][210]; char ch; int main() { cin>>n>>m>>K; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>w[i][j]>>ch; if(ch=='Y') op[i][j]=1; } } for(int j=1;j<=m;j++){ int cnt=0; for(int i=n;i>=1;i--){ if(op[i][j]) s[j][cnt][1]+=w[i][j]; else{ ++cnt; s[j][cnt][0]=s[j][cnt][1]=s[j][cnt-1][1]+w[i][j]; } } } for(int i=1;i<=m;i++){ for(int j=0;j<=K;j++){ for(int k=0;k<=n&&k<=j;k++){ dp[i][j][1]=max(dp[i][j][1],dp[i-1][j-k][1]+s[i][k][1]); //dp[i-1][j-k][1]一定会剩下一枚子弹,用于打第i列的第k个标记为Y的砖块,打完后同样会剩下一枚子弹 if(k) dp[i][j][0]=max(dp[i][j][0],dp[i-1][j-k][1]+s[i][k][0]); //第i列借一发子弹给前i-1列,最终打在第i列上 if(j>k) dp[i][j][0]=max(dp[i][j][0],dp[i-1][j-k][0]+s[i][k][1]); //前i-1列借一发子弹给第i列,最终打在前i-1列上 } } } cout<<dp[m][K][0]<<endl; return 0; }