【问题描述】
经典的难题一道,考试的时候实在是鱼,连搜素都没有想到,吃惊的是搜索加点剪枝能过6个点。。。。。。
前3个点很简单,以所有第一行的点为起点多源BFS,看能不能把最后一排的格子都遍历到。
第4个点开始,就必须要用搜索了,可以搜起点行的子集,然后从选择的点当中多源BFS一次看能不能把最后一排走完,记录最少的选择数。这里还可以加剪枝,只搜到上次最少的选择数即可。运气好能过到6个点。
然而这个图很大,搜索终究不是一个办法,观察这个图,其实有一个很重要的性质,假如能达到题目要求,就是第一行的点能遍历到的最后一行的点必然是连续的。以下简单证明一下:
用反证法,如果不连续,则必然在最后一行有一点的海拔比相邻两个点都要高。
那么水不能从相邻的两个点流到这个点,而且也不能这个点正上方的那个点流下来,也就是说,这个点被三面堵死了,不可能再有其他起点的水能够流到这个格子,不能满足题目要求,与假设矛盾。
所以第一行的点的水能流到最后一行的格子必然是一段区间,贪心用区间覆盖[1,j]的最少区间数就是答案。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define maxn 505
using namespace std;
int n,m;
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
int a[maxn][maxn];
int vis[maxn][maxn];
struct data
{
int x,y;
};
data q[maxn*maxn];
int front,rear;
struct qj
{
int x,y;
}p[maxn];
void bfs(int sx,int sy,int c)
{
front=rear=1;
q[rear++]=(data){sx,sy};
vis[sx][sy]=c;
while(front!=rear)
{
data t=q[front++];
for(int k=0;k<4;k++)
{
int ex=t.x+dx[k];
int ey=t.y+dy[k];
if(ex<=0 || ex>n || ey<=0 || ey>m)continue;
if(a[t.x][t.y]<=a[ex][ey])continue;
if(vis[ex][ey]==c)continue;
q[rear++]=(data){ex,ey};
vis[ex][ey]=c;
}
}
}
int main()
{
//freopen("flow.in","r",stdin);
//freopen("flow.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
int cc=0;
for(int j=1;j<=m;j++)
{
bfs(1,j,++cc);
for(int k=1;k<=m;k++)if(vis[n][k]==cc)
{
p[j].x=k;
break;
}
for(int k=1;k<=m;k++)if(vis[n][k]==cc)
{
p[j].y=k;
}
}
int cnt=0;
for(int j=1;j<=m;j++)if(!vis[n][j])
{
cnt++;
}
if(cnt>0)
{
printf("0\n%d\n",cnt);
}
else
{
int i=1,num=0,s=1,t=m;
while(i<=m && s<=t)
{
int last=-1;
while(i<=m && p[i].x<=s)
{
last=max(last,p[i].y);
i++;
}
if(last