题目大意:
在一个n*m棋盘中,我们把源放在第一行,源可以对外进行BFS种子填充,问:为了使得最后一行被填充完毕,我们最少需要在第一行放多少个源。假如最后一行不能被填充,问我们不能填充的个数。
解题思路:
首先,最后一行不能被填充十分简单,我们只需要在第一行逐列放源,假如最后一行还是有不能被覆盖的点的话,证明不能被填充完毕。
现在关键是第一问,最少放几个源。
结论1:当我们已知能够完全覆盖最后一行时。当源开始做flood fill,假若能覆盖到最后一行,那么覆盖到的最后一行必定是一个完整的区间(即不会产生覆盖了 [ 0,3] [5 ,7]的情况)可以用反证法证明。https://www.cnblogs.com/Tony-Double-Sky/p/9871976.html
所以,当我们可以求出第一行中每一列的能到达最后一列的区间的范围,然后我们判断最少需要多少个区间就能把最后一行覆盖。
首先,我们求第一行的每一列的所属区间范围。最简单的方法是,我们重复做BFS,但是复杂度过了,最后会T。
这里我们用DP的方法。
for nr in r_c_neighbour
memol[r][c]=min(memol[nr][nc])
其中memol[r][c]表示r,c这个点能够到达的最后一行的最左边的点的位置。nr,nc表示r,c的四邻域
同样的
for nr in r_c_neighbour
memor[r][c]=max(memor[nr][nc])
其中memor[r][c]表示r,c这个点能够到达的最后一行的最右边的点的位置。nr,nc表示r,c的四邻域
那这里还有一个问题,边界是什么:
很显然,当我们走到最后一行时,若某一格不能再DFS下去了,那么我们认为这是我们能走的最后一个位置。
写的时候注意:
(1)首先有一些点可能是到达不了最后一行的,注意这些点的状态的赋值。不要因为错误的赋值,使得转移方程出错
(2)在DFS中,我们很重要的一环是防止走重复,在这里我们注意有两种走重复,一种是memo已经赋值的了,那么我们返回memo即可,另外则是memo还未赋值,这时候我们同样需要返回。
关于区间覆盖:
这是一种贪心的做法,我们从最左边的端点开始,要保证能够把最左边的点覆盖。然后,我们选择能够覆盖最左边的点中的区间能够达到最右的区间,每次都这样贪心选取。
#include <bits/stdc++.h>
using namespace std;
const int MAXN=510;
int chess[MAXN][MAXN];
pair<int,int> memo[MAXN][MAXN];
int vis[MAXN][MAXN];
int n,m;
const int dr[]={0,-1,0,1};
const int dc[]={-1,0,1,0};
pair<int,int> dfs(int r,int c){
if(memo[r][c].first!=-1){
vis[r][c]=1;
return memo[r][c];}
if(vis[r][c]==1)return make_pair(-1,-1);
vis[r][c]=1;
int fir=1e9;
int sec=-1;
for(int i=0;i<4;i++){
int nr=r+dr[i];
int nc=c+dc[i];
if(nr<0||nr==n || nc<0||nc==m)continue;
if(chess[nr][nc]>=chess[r][c])continue;
pair<int,int> ret=dfs(nr,nc);
if(ret.first==-1)continue;
fir=min(ret.first,fir);
sec=max(ret.second,sec);
}
memo[r][c].first=fir;
memo[r][c].second=sec;
if(r==n-1 && memo[r][c].first>c)memo[r][c].first=c;
if(r==n-1 && memo[r][c].second<c)memo[r][c].second=c;
return memo[r][c];
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
cin>>chess[i][j];
}
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
memo[i][j].first=memo[i][j].second=-1;
}
memset(vis,0,sizeof(vis));
for(int i=0;i<m;i++){
dfs(0,i);
}
int flag=0;
int cnt=0;
for(int i=0;i<m;i++){
if(vis[n-1][i]==0){
flag=1;
cnt++;
}
}
if(flag){
cout<<0<<endl;
cout<<cnt<<endl;
return 0;
}
int lf=0;
cout<<1<<endl;
cnt=0;
while(lf<=m-1){
cnt++;
int maxr=-1;
for(int i=0;i<m;i++){
if(memo[0][i].first==1e9)continue;
if(memo[0][i].first<=lf){
maxr=max(maxr,memo[0][i].second);
}
}
assert(maxr!=-1);
lf=maxr+1;
}
cout<<cnt<<endl;
return 0;
}