2024年9月GESP真题及题解(C++七级): 矩阵移动

题目描述
小杨有一个 n × m n \times m n×m 的矩阵,仅包含 01? 三种字符。矩阵的行从上到下编号依次为 1 , 2 , ... , n 1,2,\dots, n 1,2,...,n,列从左到右编号依次为 1 , 2 , ... , m 1, 2, \dots, m 1,2,...,m。小杨开始在矩阵的左上角 ( 1 , 1 ) (1,1) (1,1),小杨只能向下或者向右移动,最终到达右下角 ( n , m ) (n, m) (n,m) 时停止,在移动的过程中每经过一个字符 1 得分会增加一分(包括起点和终点),经过其它字符则分数不变。小杨的初始分数为 0 0 0 分。
小杨可以将矩阵中不超过 x x x 个字符 ? 变为字符 1。小杨在修改矩阵后,会以最优的策略从左上角移动到右下角。他想知道自己最多能获得多少分。
输入格式
第一行包含一个正整数 t t t,代表测试用例组数,接下来是 t t t 组测试用例。对于每组测试用例,一共 n + 1 n + 1 n+1 行。
第一行包含三个正整数 n , m , x n, m, x n,m,x,含义如题面所示。
之后 n n n 行,每行一个长度为 m m m 的仅含 01? 的字符串。
输出格式
对于每组测试用例,输出一行一个整数,代表最优策略下小杨的得分最多是多少。
输入输出样例 1
输入 1
2
3 3 1
000
111
01?
3 3 1
000
?0?
01?
输出 1
4
2
说明/提示
样例 1 解释
对于第二组测试用例,将 ( 2 , 1 ) (2,1) (2,1) 或者 ( 3 , 3 ) (3,3) (3,3) 变为 1 1 1 均是最优策略。
数据规模与约定
| 子任务编号 | 数据点占比 | t t t | n , m n,m n,m | x x x |
|---|---|---|---|---|
| 1 1 1 | 30 % 30\% 30% | ≤ 5 \leq 5 ≤5 | ≤ 10 \le 10 ≤10 | = 1 =1 =1 |
| 2 2 2 | 30 % 30\% 30% | ≤ 10 \le 10 ≤10 | ≤ 500 \le 500 ≤500 | ≤ 30 \le 30 ≤30 |
| 3 3 3 | 40 % 40\% 40% | ≤ 10 \le 10 ≤10 | ≤ 500 \le 500 ≤500 | ≤ 300 \le 300 ≤300 |
对全部的测试数据,保证 1 ≤ t ≤ 10 1 \leq t \leq 10 1≤t≤10, 1 ≤ n , m ≤ 500 1 \leq n,m \leq 500 1≤n,m≤500, 1 ≤ x ≤ 300 1 \leq x \leq 300 1≤x≤300,保证所有测试用例 n × m n \times m n×m 的总和不超过 2.5 × 10 5 2.5 \times 10^5 2.5×105。
思路分析
这是一个动态规划问题,需要在小杨从矩阵左上角到右下角的移动过程中,通过将不超过x个'?'变为'1'来最大化得分。小杨只能向下或向右移动,每经过一个'1'(包括起点和终点)得一分。
动态规划思路
定义状态 dp[i][j][k] 表示从起点 (1,1) 走到位置 (i,j),且恰好修改了 k 个'?'变为'1'时能够获得的最大得分。
状态转移考虑两种情况:
- 从上方
(i-1,j)或左方(i,j-1)转移过来,不修改当前格子 - 如果当前格子是'?'且还有修改次数,可以将其修改为'1',从上方或左方转移并使用一次修改
由于小杨可以选择任意不超过x次修改,最终答案是 max(dp[n][m][k]) 对所有 0 ≤ k ≤ x。
代码实现
cpp
#include <bits/stdc++.h>
using namespace std;
// 定义最大常量,比题目限制稍大避免越界
const int MAXN = 502; // 最大行数
const int MAXM = 502; // 最大列数
const int MAXX = 302; // 最大修改数
// dp[i][j][k]表示走到(i,j)位置,已经修改了k个'?'时的最大得分
int dp[MAXN][MAXM][MAXX];
string s[MAXN]; // 存储矩阵,每行一个字符串
int main() {
int t; // 测试用例组数
cin >> t;
while (t--) { // 处理每组测试用例
int n, m, x; // 矩阵行数、列数、最大修改次数
cin >> n >> m >> x;
// 读取矩阵并调整下标从1开始
for (int i = 1; i <= n; i++) {
cin >> s[i];
s[i] = " " + s[i]; // 在字符串前加空格,使列下标从1开始
}
// 初始化DP数组为0(因为得分为非负整数)
memset(dp, 0, sizeof(dp));
// 动态规划计算所有状态
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 0; k <= x; k++) { // 枚举可能的修改次数
// 从上方(i-1,j)和左方(i,j-1)转移,取最大值作为前驱状态
int from_above = dp[i-1][j][k];
int from_left = dp[i][j-1][k];
int best_prev = max(from_above, from_left);
// 情况1:不修改当前格子
if (s[i][j] == '1') {
// 当前格子是'1',得分加1
dp[i][j][k] = max(dp[i][j][k], 1 + best_prev);
} else {
// 当前格子是'0'或'?'但不修改,得分不变
dp[i][j][k] = max(dp[i][j][k], best_prev);
}
// 情况2:如果当前格子是'?'且还有修改次数,可以选择修改它
if (k > 0 && s[i][j] == '?') {
// 从修改次数为k-1的状态转移
int best_prev_mod = max(dp[i-1][j][k-1], dp[i][j-1][k-1]);
// 修改当前格子变为'1',得分加1
dp[i][j][k] = max(dp[i][j][k], 1 + best_prev_mod);
}
}
}
}
// 在所有可能的修改次数中寻找最大得分
int ans = 0;
for (int k = 0; k <= x; k++) {
ans = max(ans, dp[n][m][k]);
}
cout << ans << endl;
}
return 0;
}
功能分析
1. 数据结构设计
- 三维DP数组:
dp[i][j][k]记录状态值 - 字符串数组:存储原始矩阵,下标从1开始便于处理边界
2. 边界处理
- 数组下标从1开始,
dp[0][j][k]和dp[i][0][k]自然为0(未初始化部分) - 当
i=1且j=1时,best_prev为0,正确处理起点
3. 状态转移逻辑
- 对每个位置
(i,j)和每种修改次数k,考虑两种决策:- 不修改当前格子:从上方或左方继承相同修改次数的状态
- 修改当前格子(仅当格子为'?'且k>0):从修改次数少1的状态转移
- 通过取max操作保证每一步都选择最优路径
4. 时间复杂度分析
- 时间复杂度:O(t × n × m × x)
- 空间复杂度:O(n × m × x)
各种学习资料,助力大家一站式学习和提升!!!
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"########## 一站式掌握信奥赛知识! ##########";
cout<<"############# 冲刺信奥赛拿奖! #############";
cout<<"###### 课程购买后永久学习,不受限制! ######";
return 0;
}
1、csp信奥赛高频考点知识详解及案例实践:
CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转
CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转
信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html
2、csp信奥赛冲刺一等奖有效刷题题解:
CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转
3、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html
4、CSP信奥赛C++竞赛拿奖视频课:
https://edu.csdn.net/course/detail/40437 点击跳转

· 文末祝福 ·
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"跟着王老师一起学习信奥赛C++";
cout<<" 成就更好的自己! ";
cout<<" csp信奥赛一等奖属于你! ";
return 0;
}