队内训练3 CCPC网络赛补题 dp打砖块
题目链接link.
dp难啊dp难
题意:给你一个n*m的砖块矩阵,给你k个子弹,对于每一列来说你只能从下往上打砖块,每个砖块会奖励一些糖果,有些砖块不仅会奖励糖果还会奖励一枚子弹。问你,你最多能拿多少块糖果。(好像是糖果我不记得了,反正是价值一类的东西
直接说状态转移吧,反正也是看题解写的。面向题解补题快进到下次出原题还不会嘻嘻。
首先设 d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] dp[i][j][0/1]代表前i列有j颗净消耗子弹能得到的最大价值,1代表每一列最上方都是有奖励子弹的方案,其余的情况用0表示。然后捏,这其实是一个分组背包问题加了些限制而已。只要特殊考虑以下每一列最后一个选的都是有奖励的情况就好,因为这种情况下子弹的净消耗是k-1.
一定要记得初始化为负无穷不然wa成傻逼
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
int value[210][210];
int cost[210][210];
bool ok[210][210];
int dp[210][410][2];
signed main( )
{
int t;
scanf("%lld",&t);
while(t--)
{
int n,m,K;
scanf("%lld%lld%lld",&n,&m,&K);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int v;
char c;
scanf("%lld %c",&v,&c);
value[j][i]=v;
if(c=='Y') ok[j][i]=1;
else if(c=='N') ok[j][i]=0;
}
}
for(int i=1;i<=m;i++)
value[i][n+1]=0,cost[i][n+1]=0;
for(int i=1;i<=m;i++)
{
for(int j=n;j>=1;j--)
{
cost[i][j]=cost[i][j+1]+1;
if(ok[i][j]==1) cost[i][j]--;
value[i][j]=value[i][j+1]+value[i][j];
}
}
for(int i=0;i<=m;i++)
{
for(int j=0;j<=K;j++)
{
dp[i][j][0]=-9999999999;
dp[i][j][1]=0;
}
}
for(int i=1;i<=m;i++)
{
for(int j=0;j<=K;j++)
dp[i][j][0]=dp[i-1][j][0],dp[i][j][1]=dp[i-1][j][1];
for(int j=K;j>=0;j--)
{
for(int k=1;k<=n;k++)
{
if(j>=cost[i][k])
{
if(ok[i][k]==1)
{
dp[i][j][0]=max(dp[i][j][0],dp[i-1][j-cost[i][k]][0]+value[i][k]);
dp[i][j][1]=max(dp[i][j][1],dp[i-1][j-cost[i][k]][1]+value[i][k]);
}
else if(ok[i][k]==0)
{
dp[i][j][0]=max(dp[i][j][0],dp[i-1][j-cost[i][k]][0]+value[i][k]);
dp[i][j][0]=max(dp[i][j][0],dp[i-1][j-cost[i][k]][1]+value[i][k]);
}
}
}
}
}
int ans=max( dp[m][K][0] , dp[m][K-1][1] );
printf("%lld\n",ans);
}
return 0;
}