【LGR-234-Div.3】洛谷网校 7 月 CSP-J 模拟月赛 Cfz Round 6 「Cfz Round 6」Imaichi
题目重现
原题链接
题目背景
わがままで生きるくらいが ちょうどいい
随心任性而活 这样就好
笑っていたい いまいちでもいい
我想要微笑 就算不够完美也好
题目描述
Yuki 喜欢旅行。不过她是个宅女,所以她打算在提瓦特大陆旅行。
提瓦特大陆可以被看做一个n行 m 列的方格图,每个方格内都有一个整数 ai,j。我们用(i,j)表示第 i 行第 j 列的方格。
初始时,Yuki 有s个摩拉。她会从方格图的第1行选择一个方格作为旅程起点,开始她的旅程。
接下来,Yuki 可以进行若干次移动:
- 如果 Yuki 位于方格图的前 (n−1) 行,则她可以移动到她左侧(如果存在)、右侧(如果存在)、下侧的方格;
- 如果 Yuki 位于方格图的第 n 行,则她不可以再移动。
每次移动后,Yuki 的摩拉数量都会根据她当前位于的方格而变化。具体地,设Yuki移动后位于的方格为 (i,j),则她的摩拉数量会发生如下的变化:
- 如果 ai,j>0,则Yuki的摩拉数量会增加ai,j;
- 如果 ai,j<0,则Yuki的摩拉数量会减少∣ai,j∣,即减少−ai,j;
- 如果 ai,j=0,则Yuki的摩拉数量不会发生变化。
Yuki可以重复经过同一个方格,并且在她每次经过某个方格时,她的摩拉数量都会变化。
如果在某次移动后,Yuki的摩拉数量变成了负数,则她会被拘留,不可以再移动。
特殊地,Yuki初始位于旅程起点时,她的摩拉数量也会根据她当前位于的方格而变化。同时,由于Yuki的背包大小有限,如果在某次移动后,她的摩拉数量大于k,则她的摩拉数量会变为k。
如果Yuki到达了方格图的第n行且Yuki 的摩拉数量不为负数,则我们称Yuki完成了她的旅程。
你需要帮助 Yuki 判断,她是否可以完成她的旅程;如果可以,你还需要求出,在她完成她的旅程后,她的摩拉数量的最大值。
输入格式
本题有多组测试数据。
第一行包含两个整数c,T,分别表示测试点编号和测试数据组数。样例满足c=0。
接下来依次输入每组测试数据。对于每组测试数据:
- 第一行包含四个整数n,m,s,k。
- 接下来n行,每行包含m个整数,其中第i行的第j个整数表示ai,j。
输出格式
对于每组测试数据,输出一行,包含一个整数:
- 如果Yuki可以完成她的旅程,则输出在她完成她的旅程后,她的摩拉数量的最大值;
- 如果Yuki不可以完成她的旅程,则输出−1。
输入输出样例
输入
0 2 3 3 1 5 2 -1 0 -3 -1 -1 -1 1 -2 2 3 1 3 -3 1 -1 0 -3 -2
输出
4 -1
说明/提示
样例 1 解释
对于第 1 组测试数据:
- 其中一种满足要求的移动路线为:(1,1)→(1,2)→(1,1)→(1,2)→(1,1)→(1,2)→(2,2)→(3,2);
- 在移动过程中,Yuki 的摩拉数量的变化为:1(初始时的摩拉数量)→3→2→4→3→5→4→3→4;
- 可以证明,在Yuki完成她的旅程后,她的摩拉数量的最大值为4。
对于第 2 组测试数据,显然 Yuki 无法完成她的旅程。
样例 2
见题目附件中的 journey/journey2.in 与 journey/journey2.ans。
该组样例满足测试点 4 的限制。
样例 3
见题目附件中的 journey/journey3.in 与 journey/journey3.ans。
该组样例满足测试点 8 的限制。
样例 4
见题目附件中的 journey/journey4.in 与 journey/journey4.ans。
该组样例满足测试点 10 的限制。
样例 5
见题目附件中的 journey/journey5.in 与 journey/journey5.ans。
该组样例满足测试点 14 的限制。
样例 6
见题目附件中的 journey/journey6.in 与 journey/journey6.ans。
该组样例满足测试点 15 的限制。
样例 7
见题目附件中的 journey/journey7.in 与 journey/journey7.ans。
该组样例满足测试点 16 的限制。
样例 8
见题目附件中的 journey/journey8.in 与 journey/journey8.ans。
该组样例满足测试点 20 的限制。
数据范围
对于所有测试数据:
- 1≤T≤7;
- 2≤n,m≤1000;
- 0≤s≤k≤109;
- −109≤ai,j≤109。
测试点编号 | n≤ | m≤ | 特殊性质 |
---|---|---|---|
1 | 2 | 2 | A |
2 | 2 | 2 | 无 |
3 | 50 | 50 | C |
4∼5 | 50 | 50 | 无 |
6 | 200 | 200 | A |
7 | 200 | 200 | B |
8∼9 | 200 | 200 | C |
10∼11 | 200 | 200 | 无 |
12 | 1000 | 2 | 无 |
13 | 2 | 1000 | 无 |
14 | 1000 | 1000 | A |
15 | 1000 | 1000 | B |
16∼17 | 1000 | 1000 | C |
18∼20 | 1000 | 1000 | 无 |
- 特殊性质A:保证ai,j≤0。
- 特殊性质B:保证k=0。
- 特殊性质C:保证不存在i,j满足1≤i,j<n且ai,j+ai,j+1>0。
提示
本题输入量较大,请使用较快的输入方式。
附件下载
journey.zip48.39MB
题意简述
- 旅程起点:第1行某个方格 初态
- 移动方向:左/右/下 这对应着状态转移方程
- 途径获利:获该方格对应摩拉(达k饱和)
- 旅程完成:到达第n行某个方格
- 目标:是否可以完成旅程,完成旅程后所获摩拉的最大值
题目解答
解题关键
动态规划
状态定义
f[i][j]表示走到(i,j)所获摩拉的最大值(走不到用−1表示)。
状态转移方程
初态(第1行):f(1,i)=min(a(1,i)+s,k)
下 (i,j)→(i+1,j)
f(i,j)=max(f(i,j),min(f(i−1,j)+a(i,j),k))
左/右(不换行) (i,j1)→(i,j2)
1.路径单纯往一个方向进行转移
右往左扫,再从左往右扫
左:f(i,j)=min(f(i,j−1)+a(i,j),k)
右:f(i,j)=min(f(i,j+1)+a(i,j),k)
2.绕圈
(1)如果这个圈非正可以贪心去除。
(2)如果它绕了一个正圈,一个绕圈路径可以分成很多连续段,也就是有一个连续段之和为正,也就是存在两个相邻的数,它们的和为正。“刷摩拉”,一直达到上界k。那么可以直接把这两个格子中a非负的格子的a改成k。(比如 [−2,3]→[−2,k],[0,1]→[k,k])那么从(i,j1)到(i,j2)要么是直接走,要么就是需要到某个格子刷摩拉走一条左侧的折线或者右侧的折线,来刷摩拉。具体实现从左往右扫,从右往左扫,再从左往右扫即可。
(3)但是这里没有考虑既往左又往右的情况。如果左侧有两个刷币点,那么能形如x→y→x→y…的路径。够前往最近的,就可以完成满币,不必前往下一个点。右侧同理,所以对于A…S…T…B的样子,S是入口,T是出口,A,B是刷币点,可能走出来SABT,SAT的路线,本质就是看A→T,B→T哪个更优。每一次转折都一定要刷币,而关键的刷币点只有两个,所以最多转折两次,于是代码就是从左往右扫,从右往左扫,从左往右扫,从右往左扫,从左往右扫。
答案ans=max f(n,j)。
题解代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,s;ll k;
const int MAXN=1005;
int a[MAXN][MAXN];
ll f[MAXN][MAXN];
ll ans;
//快读
void read(int &a){char c;a=0;int f=1;while(!isdigit(c=getchar()))if(c=='-')f=-1;do a=a*10+c-'0';while(isdigit(c=getchar()));a*=f;
}
//快写
void write(int a){if(a<0)putchar('-'),a=-a;if(a>=10)write(a/10);putchar('0'+a%10);
}
void lr(int i){for(int j=1;j<=m;j++){if(j-1>=1&&f[i][j-1]>=0)f[i][j]=max(f[i][j],min(f[i][j-1]+a[i][j],k));}
}
void rl(int i){for(int j=m;j>=1;j--){if(j+1<=m&&f[i][j+1]>=0)f[i][j]=max(f[i][j],min(f[i][j+1]+a[i][j],k));}
}
signed main(){int c,T;cin>>c>>T;while(T--){cin>>n>>m>>s>>k;for(int i=1;i<=n;i++)for(int j=1;j<=m;++j)read(a[i][j]);for(int i=1;i<=n;i++)for(int j=1;j<=m;++j)f[i][j]=-1;for(int i=1;i<=n;i++){if(i==1){for(int j=1;j<=m;j++){f[i][j]=max(f[i][j],min((ll)s+a[i][j],k));}}else{for(int j=1;j<=m;j++){if(f[i-1][j]>=0)f[i][j]=max(f[i][j],min(f[i-1][j]+a[i][j],k));}}if(i==n)break;for(int j=1;j<=m-1;j++){if(a[i][j]+a[i][j+1]>0){if(a[i][j]>=0)a[i][j]=k;if(a[i][j+1]>=0)a[i][j+1]=k;}}lr(i);rl(i);lr(i);rl(i);lr(i);}ans=-1;for(int j=1;j<=m;j++)ans=max(ans,f[n][j]);cout<<ans<<endl;}return 0;
}