题解 P1174 【打砖块】
查看原题请戳这里
导入
我们先来看一个看似正确的分组背包的方法:
我们将每一列拆分为n个物品,第
简单的说,第
然后,我们就以此来跑一边分组背包,于是就愉快的暴0了。
为什么呢?
因为如果当前一个标记为
正解
预处理
我们可以发现,如果我们当前有
由于当某个标记为
状态设计
我们用
状态转移
先贴一波代码:
for(int i = 1; i <= m; i++)
for(int j = 0; j <= k; j++)
for(int l = 0; l <= min(n,j); l++)
{
f[i][j][1] = max(f[i][j][1],f[i - 1][j - l][1] + v[i][l][1]);
if(l) f[i][j][0] = max(f[i][j][0],f[i - 1][j - l][1] + v[i][l][0]);
if(j > l) f[i][j][0] = max(f[i][j][0],f[i - 1][j - l][0] + v[i][l][1]);
}
其中
f[i][j][1] = max(f[i][j][1],f[i - 1][j - l][1] + v[i][l][1]);
这个转移是说我从
if(l) f[i][j][0] = max(f[i][j][0],f[i - 1][j - l][1] + v[i][l][0]);
这个转移是说如果我分配给了第
if(j > l) f[i][j][0] = max(f[i][j][0],f[i - 1][j - l][0] + v[i][l][1]);
这个转移是说如果我让第
注:在前两段中所说的消耗子弹是指打完某些砖块后总子弹数变少,只打标记为
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
#define INF 0x7fffffff
#define re register
using namespace std;
int read()
{
register int x = 0,f = 1;register char ch;
ch = getchar();
while(ch > '9' || ch < '0'){if(ch == '-') f = -f;ch = getchar();}
while(ch <= '9' && ch >= '0'){x = x * 10 + ch - 48;ch = getchar();}
return x * f;
}
int n,m,k,cnt,a[205][205],b[205][205],v[205][205][2],f[205][205][2];
char c;
int main()
{
n = read(); m = read();k = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
cin >> a[i][j] >> c;
if(c == 'Y') b[i][j] = 1;
}
for(int i = 1; i <= m; i++)
{
cnt = 0;
for(int j = n; j >= 1; j--)
{
if(b[j][i]) v[i][cnt][1] += a[j][i];
else cnt++,v[i][cnt][1] = v[i][cnt - 1][1] + a[j][i], v[i][cnt][0] = v[i][cnt - 1][1] + a[j][i];
}
}
for(int i = 1; i <= m; i++)
for(int j = 0; j <= k; j++)
for(int l = 0; l <= min(n,j); l++)
{
f[i][j][1] = max(f[i][j][1],f[i - 1][j - l][1] + v[i][l][1]);
if(l) f[i][j][0] = max(f[i][j][0],f[i - 1][j - l][1] + v[i][l][0]);
if(j > l) f[i][j][0] = max(f[i][j][0],f[i - 1][j - l][0] + v[i][l][1]);
}
printf("%d\n",f[m][k][0]);
return 0;
}