P1174 打砖块
题目在这里
思路
思路2分钟,调试9小时。
每列都可以分配随机个数的子弹,就是分组背包了,每列为一组,物品用前缀和表示。
就是如果有奖励子弹的话空间就不增加。
还要考虑如果这列最后打的是奖励子弹的砖块,那么其实需要到其他地方借一个,打完就能还,所以只要有一列最后是N的砖块这个方案即为满足。
设计
剩下的亿点细节在代码中。
code
//这题最坑的在于空间可能为零,我们通常的直接二维背包写法会死得很惨!
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=410;
int read(){
int x=0,f=1;char c=getchar();
while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
int ans,mp[N][N],n,m,x,bk[N][N],va[N][N],v[N][N],f[N][N][2];
signed main(){
n=read(),m=read(),x=read();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
mp[i][j]=read();
char c;cin>>c;
if(c=='Y')bk[i][j]=1;//第一维就是行,第二维就是列,开始时不同数组变来变去把自己搞懵了
}
}
for(int i=n;i>=1;i--){
for(int j=1;j<=m;j++){
va[i][j]=va[i+1][j]+mp[i][j];
if(!bk[i][j])v[i][j]=v[i+1][j]+1;
else v[i][j]=v[i+1][j];//对物品预处理
}
}
for(int i=0;i<=m;i++)
for(int j=0;j<=x;j++)
for(int l=0;l<=1;l++)
f[i][j][l]=-1e9;//给f数组设初值,用memset见祖宗
for(int i=0;i<=m;i++)f[i][0][0]=0;//为啥我开始时写从1循环
for(int k=1;k<=m;k++){
for(int j=x;j>=0;j--)f[k][j][0]=max(f[k][j][0],f[k-1][j][0]),f[k][j][1]=max(f[k][j][0],f[k-1][j][1]);//继承上一组达到的最优解!
for(int j=x;j>=0;j--){
for(int i=n;i>=1;i--){
if(v[i][k]>j)continue;
if(!bk[i][k])f[k][j][1]=max(f[k][j][1],max(f[k-1][j-v[i][k]][0]+va[i][k],f[k-1][j-v[i][k]][1]+va[i][k]));
else{
f[k][j][1]=max(f[k][j][1],f[k-1][j-v[i][k]][1]+va[i][k]);
f[k][j][0]=max(f[k][j][0],f[k-1][j-v[i][k]][0]+va[i][k]);
if(j>=v[i][k]+1)f[k][j][1]=max(f[k][j][1],max(f[k-1][j-v[i][k]-1][0]+va[i][k],f[k-1][j-v[i][k]-1][1]+va[i][k]));//你要是真的有子弹,多给一发不借也行啊
}
}
}
}
for(int i=0;i<=x;i++)ans=max(ans,f[m][i][1]);//不一定子弹多就一定能打最优解,可能多余的用都用不了
printf("%lld\n",ans);
return 0;
}