【NOIP2010】引水入城

【NOIP2010】引水入城

这道题从高到低建图,因为有无解的情况,所以从海边可建造蓄水池的地方开始搜索,如果最后一行有搜不到的,那么直接统计一下个数,输出就好。显然搜索不能dfs,深度太大会爆栈啊,所以bfs也可以实现。至于有解的情况,我们考虑一个性质,一个点向下传递,在最后一行能够便利到的点一定是联通的,否则的话是无解的情况,所以我们仍然以每个蓄水池搜索,算出其覆盖最后一行的左右区间,然后dp。

还有一种方法,将每一个蓄水池的覆盖范围看成一条线段,算出区间端点后,对于A,B两个线段,若B的左端点<=A的右端点+1,两个左端点连一条权值为1的边,跑1->m的最短路就行,至于为什么,自己画图体会,很容易理解。

  1 #define MAXN 550UL
  2 
  3 #include<cstdio>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<queue>
  7 #define MIN(a,b) (a)<(b)?(a):(b);
  8 using namespace std;
  9 int n,m,cnt,head[MAXN*MAXN];
 10 int H[MAXN][MAXN],ID[MAXN][MAXN],ans,la;
 11 bool vis[MAXN*MAXN],flag=0;
 12 int f[MAXN];
 13 int Ls[MAXN],Rs[MAXN];
 14 struct Node{
 15     int be,en,to;
 16 }eda[1000110];
 17 void Add(int x,int y){
 18     eda[++cnt].be=x;
 19     eda[cnt].en=y;
 20     eda[cnt].to=head[x];
 21     head[x]=cnt;
 22 }
 23 struct XS{
 24     int id,h;
 25 }D[MAXN*MAXN];
 26 int cop(const XS a,const XS b){
 27     return a.h>b.h;
 28 }
 29 
 30 void BFS(int t){
 31     memset(vis,0,sizeof(vis));
 32     queue<int>st;
 33     if(t==0)for(int i=1;i<=m;i++)   st.push(i),vis[i]=1;
 34     else {
 35         vis[t]=1;
 36         st.push(t);
 37     }
 38     while(!st.empty()){
 39         int x=st.front();
 40         st.pop();
 41         for(int i=head[x];i;i=eda[i].to){
 42             int y=eda[i].en;
 43             if(!vis[y]){
 44                 vis[y]=1;
 45                 st.push(y);
 46             }
 47         }
 48     }
 49 }
 50 int main(){
 51     freopen("flow.in","r",stdin);
 52     freopen("flow.out","w",stdout);
 53     scanf("%d%d",&n,&m);
 54     for(int i=1;i<=n;i++){
 55         for(int j=1;j<=m;j++){
 56             scanf("%d",&H[i][j]);
 57             ID[i][j]=(i-1)*m+j;
 58         }
 59     }
 60     for(int i=1;i<=n;i++){
 61         for(int j=1;j<=m;j++){
 62             if(H[i][j]>H[i-1][j]&&i>1) Add(ID[i][j],ID[i-1][j]);
 63             if(H[i][j]>H[i+1][j]&&i<n) Add(ID[i][j],ID[i+1][j]);
 64             if(H[i][j]>H[i][j-1]&&j>1) Add(ID[i][j],ID[i][j-1]);
 65             if(H[i][j]>H[i][j+1]&&j<m) Add(ID[i][j],ID[i][j+1]);
 66         }
 67     }
 68     BFS(0);
 69     bool ok=0; int lr=0;
 70     for(int i=1;i<=m;i++){
 71         if(!vis[ID[n][i]]){
 72             ok=1;
 73             lr++;
 74         }
 75     }
 76     if(ok){
 77         printf("0\n%d",lr);
 78         return 0;
 79     }
 80     else{
 81         for(int i=1;i<=m;i++){
 82              Ls[i]=m+1;
 83              Rs[i]=0;
 84             BFS(i);
 85             for(int j=1;j<=m;j++){
 86                 if(vis[ID[n][j]]){
 87                     if(j<Ls[i]) Ls[i]=j;
 88                     if(j>Rs[i]) Rs[i]=j;
 89                 }
 90             }
 91         }
 92         memset(f,30,sizeof(f));
 93         f[0]=0;
 94         for(int i=1;i<=m;i++){
 95         //    printf("%d %d\n",Ls[i],Rs[i]);
 96             for(int j=1;j<=m;j++){
 97                 if(Ls[j]<=i&&Rs[j]>=i){
 98                     f[i]=MIN(f[i],f[Ls[j]-1]+1);
 99                 }
100             }
101         }
102         printf("1\n%d",f[m]);
103     }
104     getchar(); getchar(); 
105 }
View Code

 

posted @ 2015-10-09 20:18 Lenicodes 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值