[题解] [洛谷 P1174] 打砖块

wxy3265·2024-04-20 17:54·58 次阅读

[题解] [洛谷 P1174] 打砖块

[洛谷 P1174] 打砖块

题目描述#

nm 列的砖块和 k 发子弹,每个砖块都有一个得分,每次可以用一发子弹打碎某一列最下面的砖块并得到相应的得分。有的砖块在打碎后可以获得一发额外子弹的奖励。求该游戏的最大得分。

image

输入格式#

第一行有 3个正整数, n,m,k 。表示开始的时候,有 nm 列的砖块,小红有 k 发子弹。

接下来有 n 行,每行的格式如下:

f1c1f2c2f3c3...fmcm

其中 fi 为正整数,表示这一行的第 i 列的砖,在打碎以后的得分。ci 为一个字符,只有两种可能,Y 或者 N。Y 表示有一发奖励的子弹,N 表示没有。

所有的数与字符之间用一个空格隔开,行末没有多余的空格。

输出格式#

仅一个正整数,表示最大的得分。

题解#

不难看出该题需要使用动态规划解决。设计状态 dpi,j 表示前i列,用j颗子弹能够得到的最高分数。使用前缀和 sumi,j 表示打掉第 i 行第 j 列能够得到的分数。此时不难得出状态转移方程: dpi,j=max(dp[i1][jl]+sum[i][l]) 其中, l 为枚举的在当前列的花费。显然,这种做法在不会出现 Y 时可以应对,但在出现 Y 时就会有以下问题:对于其中一列,假设是如下情况:

1 Y
1 N

那么在打掉下面的 N 后,如果我们还剩下一颗子弹,就可以没有花费打掉 Y ,但如果我们在打完 N 后刚好用完了所有子弹,那么我们就无法再获得 Y 的分数。此外还有如下特殊情况:

1 N 1 Y
1 N 1 N

这种情况下,对于第二列,我们虽然可以以一颗子弹的花费获得两个砖块的分数,但是我们需要用到两颗子弹。因此,我们需要考虑最后一颗子弹是否在这一列打完。如果最后一颗子弹在这一列打完,我们就无法通过“借用”子弹的方式获得 Y 的分数,否则我们就可以无花费的打掉 Y 。特别的,如果我们在当前列花费的子弹数小于总子弹数,那么我们一定可以无花费的打掉 Y 。

因此,我们需要记录两种状态:dp1i,j 表示最后一颗子弹打在这一列的最高分数, dp2i,j 表示最后一颗子弹没有打在这一列的分数。相应的,我们每个方块的得分也需要分成最后一颗子弹打在这一列时每个砖块的得分 sum1i,j 和最后一颗子弹没有打在这一列时每个砖块的得分 sum2i,j 。同时对两个状态进行转移即可。

AC代码#

Copy
#include <algorithm> #include <iostream> #define int long long using namespace std; const int MAXN = 1003; int f[MAXN][MAXN]; char c[MAXN][MAXN]; int dp1[MAXN][MAXN]; //dp_i,j: 考虑前i列,花费j颗子弹,最后一颗子弹打在这一列的最高分数 int dp2[MAXN][MAXN]; //dp_i,j: 考虑前i列,花费j颗子弹,最后一颗子弹不打在这一列的最高分数 int sum1[MAXN][MAXN], sum2[MAXN][MAXN]; // sum:分数的前缀和 int n, m, k; signed main() { // 数据输入 cin >> n >> m >> k; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { cin >> f[i][j] >> c[i][j]; } } // 初始化sum for (int i = 1; i <= m; i++) { int cnt = 0; for (int j = n; j > 0; j--) { if (c[j][i] == 'Y') { sum1[i][cnt] += f[j][i]; // 这一列可以预支 } else { cnt++; sum1[i][cnt] = sum2[i][cnt] = sum1[i][cnt - 1] + f[j][i]; } } } // dp for (int j = 1; j <= m; j++) { // 列数 for (int i = 0; i <= k; i++) { // 消耗的总子弹数 for (int l = 0; l <= min(n, i); l++) { // 在当前列消耗的子弹数 dp1[j][i] = max(dp1[j][i], dp1[j - 1][i - l] + sum1[j][l]); // 如果当前这一列不是最后打到的,就可以无脑预支 if (l != 0) dp2[j][i] = max(dp2[j][i], dp1[j - 1][i - l] + sum2[j][l]); // 不预支的情况 if (l < i) dp2[j][i] = max(dp2[j][i], dp2[j - 1][i - l] + sum1[j][l]); // 一定可以预支的情况 } } } cout << dp2[m][k] << '\n'; return 0; }
posted @   wxy3265  阅读(58)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示
目录