k题大意是打砖块, n × m n\times m n×m个砖块,你只有k枚子弹,有些砖块打破后可以重新获得一枚子弹。要求计算最多能得多少分。
这显然是一道dp,如果没有“重新获得子弹”这一设定的话难度会下降很多。带上这个设定之后状态转移会比较难想。
考虑用两个矩阵来进行状态转移,dp[0][i][j]表示在[1,i]列中射击,可用j枚子弹(且最后击破的是不返还子弹型的砖块)的最高分,dp[1][i][j]表示在[1,i]列中射击,可用j枚子弹(且最后击破的是返还子弹型的砖块)的最高分。
然后就可以状态转移了,复杂度 O ( n m k ) O(nmk) O(nmk)。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 205;
int n, m, k;
int d[2][maxn][maxn], dp[2][maxn][maxn];
// d[1][i][j] stand for y
int f[maxn][maxn];
bool r[maxn][maxn];
// 0=n,1=y
void solve()
{
for (int t = 0; t < 2; ++t)
{
for (int i = 0; i < maxn; ++i)
{
memset(d[t][i], 0, sizeof(d[t][i]));
memset(dp[t][i], 0, sizeof(dp[t][i]));
}
}
for (int i = 1; i <= m; ++i)
{
int num = 0;
for (int j = n; j >= 1; j--)
{
if (r[j][i])
d[1][i][num] += f[j][i];
else
{
num++;
d[0][i][num] = d[1][i][num - 1] + f[j][i];
d[1][i][num] = d[0][i][num];
}
}
}
for (int i = 1; i <= m; ++i)
{
for (int j = 0; j <= k; ++j)
{
for (int l = 0; l <= min(j, n); ++l)
{
dp[1][i][j] = max(dp[1][i][j], dp[1][i - 1][j - l] + d[1][i][l]);
if (l)
dp[0][i][j] = max(dp[0][i][j], dp[1][i - 1][j - l] + d[0][i][l]);
if (j != l)
dp[0][i][j] = max(dp[0][i][j], dp[0][i - 1][j - l] + d[1][i][l]);
}
}
}
}
int main()
{
// freopen("kin", "r", stdin);
int tests;
scanf("%d", &tests);
while (tests--)
{
scanf("%d%d%d", &n, &m, &k);
char c;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
{
scanf("%d %c ", &f[i][j], &c);
r[i][j] = (c == 'Y');
}
solve();
printf("%d\n", dp[0][m][k]);
}
return 0;
}