首先通过佬的博客可以得到一个结论:当最后有解时,每个蓄水厂在最后一行能够覆盖到的区间一定是连续的,由此可以将其转化为区间覆盖问题进行贪心。
考虑用 p a i r pair pair 存储对于第一行的第 i i i 个点,其在最后一行能覆盖到的区间的左端点和右端点。由于最后需要贪心选择最小的区间以覆盖整个 [ 1 , m ] [1,m] [1,m] 范围,因此在每次的 d f s dfs dfs 之前都要把 v i s vis vis 数组进行清空,防止第 i i i 点的左端点答案受第 i − 1 i-1 i−1 点的右端点答案影响。而考虑到 v i s vis vis 数组的清空问题,可以在求有解答案之前,先跑一遍判断是否无解。
当 n = = 1 n==1 n==1 时将跑出死循环,因此需要进行特判。同时进行了一个优化操作:仅有第 i i i 个点能够流到第 2 2 2 行的对应点时,才会对其进行 d f s dfs dfs(其实也可以直接跑某个第一行的点是否能被之前已访问过的点覆盖,若覆盖则不用对该点进行 d f s dfs dfs ,因为该点能够产生的所有贡献都已经包含在前一个点的答案内)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=510;
int n,m,now;
int g[N][N],vis[N][N];
pair<int,int> pos[N];
void dfs(int x,int y,int st){
vis[x][y]=1;
if(x==n-1&&st){
pos[now].first=min(pos[now].first,y);
pos[now].second=max(pos[now].second,y);
}
if (x>=1&&!vis[x-1][y]&&g[x][y]>g[x-1][y])
dfs(x-1,y,st);
if (x<n-1&&!vis[x+1][y]&&g[x][y]>g[x+1][y])
dfs(x+1,y,st);
if (y>=1&&!vis[x][y-1]&&g[x][y]>g[x][y-1])
dfs(x,y-1,st);
if (y<m-1&&!vis[x][y+1]&&g[x][y]>g[x][y+1])
dfs(x,y+1,st);
}
signed main(){
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>g[i][j];
//对于没有答案时的处理
for(int i=0;i<m;i++)
dfs(0,i,0);
int ans=0;
for(int i=0;i<m;i++)
if (!vis[n-1][i])
ans++;
if(ans){
cout<<0<<endl<<ans;
return 0;
}
for(int i=0;i<m;i++){
memset(vis,0,sizeof(vis));
now=i;
pos[now].first=501;
if(n==1)//特判只有一行的情况时
dfs(0,i,1);
else if(g[0][i]>g[1][i])
dfs(1,i,1);
}
int maxx;
now=-1;
while(now<m-1){//选择最少区间覆盖整个区间的贪心问题
maxx=-1;
for(int i=0;i<m;i++)
if(pos[i].first<=now+1&&pos[i].second>maxx)
maxx=pos[i].second;
ans++;
now=maxx;
}
cout<<1<<endl<<ans;
return 0;
}