题目描述
小红很喜欢玩一个叫打砖块的游戏,这个游戏的规则如下:
在刚开始的时候,有n行*m列的砖块,小红有k发子弹。小红每次可以用一发子弹,打碎某一列当前处于这一列最下面的那块砖,并且得到相应的得分。(如图所示)
某些砖块在打碎以后,还可能将得到一发子弹的奖励。最后当所有的砖块都打碎了,或者小红没有子弹了,游戏结束。
小红在游戏开始之前,就已经知道每一块砖在打碎以后的得分,并且知道能不能得到一发奖励的子弹。小红想知道在这次游戏中她可能的最大得分,可是这个问题对于她来说太难了,你能帮帮她吗?
输入输出格式
输入格式:
第一行有3个正整数,n,m,k。表示开始的时候,有n行*m列的砖块,小红有k发子弹。
接下来有n行,每行的格式如下:
f1 c1 f2 c2 f3 c3 …… fm cm
其中fi为正整数,表示这一行的第i列的砖,在打碎以后的得分。ci为一个字符,只有两种可能,Y或者N。Y表示有一发奖励的子弹,N表示没有。
所有的数与字符之间用一个空格隔开,行末没有多余的空格。
输出格式:
仅一个正整数,表示最大的得分。
输入输出样例
输入样例#1:
3 4 2
9 N 5 N 1 N 8 N
5 N 5 Y 5 N 5 N
6 N 2 N 4 N 3 N
输出样例#1:
13
说明
对于20%的数据,满足1<=n,m<=5,1<=k<=10,所有的字符c都为N
对于50%的数据,满足1<=n,m<=200,1<=k<=200,所有的字符c都为N
对于100%的数据,满足1<=n,m<=200,1<=k<=200,字符c可能为Y
对于100%的数据,所有的f值满足1<=f<=10000
【分析】
这道题非常妙啊…虽然看上去是线性dp,但还要加一个维度来记录状态。
思路:贪心+dp。
1. 首先找出最下面一行是Y的砖块,可以免费打掉。
2. 对于Y与N交叉的地方,将一个N上面全部的连续的Y的总分累加进N所在位置,用数组now储存。每一列前缀和用res储存,最后now要加上res,表示将该点下面所有的点都选了,而且把该点上面的连续的y砖块也打了。还要预处理出来在第j列,打掉前i个砖块所需的子弹,用ci数组储存。
3. 这时候用到分组背包的思想。dp[j][i][0]表示前j列总共耗费i个子弹,且之后不用”借”子弹,dp[i][j][1]表示之后还要”借“子弹,所能得到的最大分数(“借”的意思:比如第一列是 2 Y 2 N 且N必须先打掉,那么dp[1][1][0]=2,dp[1][1][1]=4),答案即为dp[m][k][0]。
状态转移方程: dp[j][k][0]=max(dp[j][k][0],max(dp[j-1][k-ci[j][i]][1],dp[j-1][k-ci[j][i]][0])+res[j][i]);
dp[j][k][0]=max(dp[j][k][0],dp[j-1][k-ci[j][i]][0]+now[j][i]);
dp[j][k][1]=max(dp[j][k][1],dp[j-1][k-ci[j][i]][1]+now[j][i]);
初始化: for(j=0;j<=m;j++) dp[j][0][0]=-inf;
【代码】
//洛谷 打砖块
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=205;
int n,m,p,ans;
char ch;
int a[mxn][mxn],whe[mxn],res[mxn][mxn];
int dp[mxn][mxn][2],ci[mxn][mxn],now[mxn][mxn]; //0:不需要借,1:需要借
bool b[mxn][mxn];
int main()
{
int i,j,k;
scanf("%d%d%d",&n,&m,&p);
if(p==0) {printf("0\n");return 0;}
for(i=n;i>=1;i--)
fo(j,1,m)
{
scanf("%d",&a[i][j]);
cin>>ch;
if(ch=='Y') b[i][j]=1;
}
fo(j,1,m)
{
fo(i,1,n)
{
if(!b[i][j]) break;
ans+=a[i][j];
}
whe[j]=i;
}
fo(j,1,m) fo(i,whe[j],n) res[j][i]=res[j][i-1]+a[i][j];
fo(j,1,m) fo(i,whe[j],n) now[j][i]=res[j][i];
fo(j,1,m)
{
ci[j][whe[j]]=1;
fo(i,whe[j],n)
{
int tmp=i;
while(b[i+1][j]) i++;
now[j][tmp]+=res[j][i]-res[j][tmp];
ci[j][i+1]=ci[j][tmp]+1;
}
}
fo(j,0,m) dp[j][0][0]=-1e8;
fo(j,1,m)
fo(k,1,p)
{
dp[j][k][0]=max(dp[j][k][0],dp[j-1][k][0]);
dp[j][k][1]=max(dp[j][k][1],dp[j-1][k][1]);
fo(i,whe[j],n) if(!b[i][j] && k>=ci[j][i])
{
dp[j][k][0]=max(dp[j][k][0],max(dp[j-1][k-ci[j][i]][1],dp[j-1][k-ci[j][i]][0])+res[j][i]);
dp[j][k][0]=max(dp[j][k][0],dp[j-1][k-ci[j][i]][0]+now[j][i]);
dp[j][k][1]=max(dp[j][k][1],dp[j-1][k-ci[j][i]][1]+now[j][i]);
}
}
printf("%d\n",dp[m][p][0]+ans);
return 0;
}
//3 3 2
//2 N 2 N 2 N
//2 Y 2 Y 3 N
//2 N 2 N 2 Y