题意
在 n n n * m m m 的网格中,每个格子上的数字仅为 1 1 1 或 − 1 -1 −1,每次只能向下或向右走,问从左上到右下是否存在一条路径使得路径上所有数字之和正好等于 0 0 0 ?
第一反应是和 2021ICPC江西省赛A 一样的做法,三维滚动数组记录当前在第 i i i 行第 j j j 列且拥有 k k k 个 1 1 1 的状态是否存在,最后判断处于右下角时 k k k 是否等于 ( n + m − 1 ) / 2 (n+m-1)/2 (n+m−1)/2 。容易看出当 n + m n+m n+m 为偶数时可以特判无解。
然而本题的侧重点是最后和为
0
0
0 是否可行,因此每一步需要延续前一步的所有可行状态,这样的操作对我来说是很抽象的
考虑到每次的更改只能是 + 1 +1 +1 或 − 1 -1 −1,导致存在连续状态,因此可以直接维护到当前点的所有路径中能够得到的最大代价和最小代价,则处于该区间内的都是可发生状态。最后判断 0 0 0 是否在该闭区间内即可。
被 memset 超时搞麻了
本代码中修正了多组样例输入存在特判时,未全部读入完成就 r e t u r n return return 的坏习惯!
#include <bits/stdc++.h>
// #define int long long
#define endl "\n"
using namespace std;
const int N=1e3+10;
int g[N][N],a[N][N],b[N][N];
//n行m列 最后能拿到n+m-1个数字 对n+m为偶数的直接特判掉
void solve(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>g[i][j];
if((n+m)%2==0){
cout<<"NO"<<endl;
return;
}
a[1][1]=b[1][1]=0;
// memset(a,0,sizeof(a));
// memset(b,0,sizeof(b));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i==1&&j==1)
;
else if(i==1){
a[i][j]=a[i][j-1];
b[i][j]=b[i][j-1];
}
else if(j==1){
a[i][j]=a[i-1][j];
b[i][j]=b[i-1][j];
}
else{
a[i][j]=min(a[i][j-1],a[i-1][j]);
b[i][j]=max(b[i][j-1],b[i-1][j]);
}
a[i][j]+=g[i][j];
b[i][j]+=g[i][j];
}
}
// cout<<a[n][m]<<" "<<b[n][m]<<endl;
if(a[n][m]<=0&&b[n][m]>=0)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0);
int T=1;
cin>>T;
while(T--)
solve();
return 0;
}