NOIP 2010 P1514 引水入城

题目:传送门

题目概要:有一个n行m列的矩阵,每一个格子都有一个高度,路径只能从高处向低处扩散,问你如果最后一行可以全部被覆盖,最少要从第一行多少个格子开始,如果不能使最后一行全部被覆盖,求有多少个格子不能;

看完这道题,最直接的想法就是直接定义dx,dy两个数组表示上下左右走,看看第一行每一个格子能对应多少个最后一行的格子。

然后再设置一个vis数组表示最后一行是否已经被到达过,如果最后一行有点还没有被到达过,就输出0和vis=0的格子数量

但是当我们想要实现的时候,发现如果第一行的某个点对应的最后一行的点是断断续续的,那就很舒(e)服(xin)了

buuuut~

似乎可以证明,对于每一个第一行的点,他所对应的最后一行的点总是连续的

反证法:假设可以不连续

我们来看图

如图,这是一条从上到下的连续的路径,用红色标记

 现在我们假设从第一行开始有这么一条路径不连续,如图,用蓝色表示

 我们会发现,这样的话一定会有重合的路径,用紫色表示

既然这样,从第一行蓝色点出发也一定能够到达最下层的红色点

那么最下面一行的区间一定是连续的,证毕(这里不连续是因为有无解的情况)

有了这个结论,只要不是无解的情况,把最后一行的连续区间拿出来,就变成了一个线段覆盖问题

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<string>
 7 #include<queue>
 8 #include<stack>
 9 #include<time.h>
10 using namespace std;
11 typedef long long ll;
12 ll read(){
13     ll ans=0;
14     char last=' ',ch=getchar();
15     while(ch<'0' || ch>'9')last=ch,ch=getchar();
16     while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
17     if(last=='-')ans=-ans;
18     return ans;
19 }
20 
21 const int maxn=501;
22 
23 int n,m,atlas[maxn][maxn];
24 int num[maxn][maxn];
25 bool vis[maxn][maxn];
26 int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
27 int l[maxn][maxn],r[maxn][maxn];
28 
29 //记忆化搜索 
30 void dfs(int x,int y)
31 {
32     vis[x][y]=true;//先标记这个点到达过 
33     for(int i=0;i<4;i++)//上下左右搜索 
34     {
35         int nx=x+dx[i],ny=y+dy[i];
36         if(nx<1||nx>n||ny<1||ny>m) continue;//判断边界 
37         if(atlas[nx][ny]>=atlas[x][y]) continue;//判断高度 
38         if(!vis[nx][ny])dfs(nx,ny);
39         l[x][y]=min(l[x][y],l[nx][ny]);
40         r[x][y]=max(r[x][y],r[nx][ny]);//更新区间左右端点 
41     }
42 }
43 
44 int main()
45 {
46     n=read(),m=read();
47     memset(vis,false,sizeof(vis));
48     memset(l,0x3f,sizeof(l));//初始化 
49     for(int i=1;i<=m;i++)
50     {
51         l[n][i]=r[n][i]=i;//区间初始化 
52     }
53     for(int i=1;i<=n;i++)
54     {
55         for(int j=1;j<=m;j++)
56         {
57             atlas[i][j]=read();
58         }
59     }
60     
61     for(int i=1;i<=m;i++)
62     {
63         if(!vis[1][i]) dfs(1,i);//如果还没有被到达过,就搜索 
64     }
65     
66     int counti=0;
67     for(int i=1;i<=m;i++)
68     {
69         if(!vis[n][i]) counti++;
70     }//看最后一行有没有不能到达的 
71     if(counti!=0) 
72     {
73        cout<<0<<endl<<counti;
74        return 0;
75     }
76     
77     int left=1;//记录当前最左边的点 
78     while (left<=m)//跑一遍区间覆盖 
79     {
80         int maxr=0;
81         for (int i=1;i<=m;i++)
82             if (l[1][i]<=left)//如果这个点在区间左端点的右边 
83                 maxr=max(maxr,r[1][i]);//寻找右端点最大的 
84         counti++;
85         left=maxr+1;//继续更新 
86     }
87     cout<<1<<endl<<counti;
88     return 0;
89 }
原文地址:https://www.cnblogs.com/lcezych/p/10821902.html