【NOIP2010】引水入城

以前一直以为是什么高端DP,看了题解才发现是水题,老是这样看题解才能写出来到赛场上怎么办嘛QAQ

原题:

在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政
区划十分特殊,刚好构成一个 N 行 M 列的矩形,如上图所示,其中每个格子都代表一座城 市,每座城市都有一个海拔高度。
为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施 有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的 蓄水池中。因此,只有与湖泊毗邻的第 1 行的城市可以建造蓄水厂。而输水站的功能则是通 过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是 存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。
由于第 N 行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利 设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干 旱区中不可能建有水利设施的城市数目。

 

 

首先可以先假设第一行的所有格子都建站了,用bfs即可求出是否能把最后一行完全覆盖以及如果不能完全覆盖没有覆盖到的格子有几个

然后在用一个bfs求出如果在第一行第i个格子建站能在最后一行上覆盖区间的左端点和右端点,容易证明如果在第一行某点建站,在最后一行一定是连续覆盖一个区间

然后区间覆盖dp,if(i>=left[j] && i<=right[j])  f[i]=min(f[i],f[left[j]-1]+1)

需要有一个特判,就是只有一行的情况(如果在第一次bfs把第一行每个点入队的同时也标记上的话似乎就不用搞这个特判)

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 int read(){int z=0,mark=1;  char ch=getchar();
 8     while(ch<'0'||ch>'9'){if(ch=='-')mark=-1;  ch=getchar();}
 9     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
10     return z*mark;
11 }
12 int fx[4]={1,-1,0,0},fy[4]={0,0,1,-1};
13 int m,n,a[510][510];
14 int visited[510][510];
15 int dui[310000],tou=0;
16 int ans;
17 int fleft[510],fright[510];
18 int f[510];
19 int bfs1(){
20     memset(visited,0,sizeof(visited));
21     for(int i=1;i<=n;i++)  dui[++tou]=i-1;
22     for(int k=1;k<=tou;k++){
23         int x=dui[k]/n+1,y=dui[k]%n+1;
24         for(int i=0;i<4;i++)if(!visited[x+fx[i]][y+fy[i]] && a[x][y]>a[x+fx[i]][y+fy[i]]){
25             visited[x+fx[i]][y+fy[i]]=-1;
26             dui[++tou]=(x+fx[i]-1)*n+y+fy[i]-1;
27         }
28     }
29     int ge=0;
30     for(int i=1;i<=n;i++)  if(!visited[m][i])  ge++;
31     return ge;
32 }
33 void bfs2(int _s){
34     dui[tou=1]=_s-1;
35     for(int k=1;k<=tou;k++){
36         int x=dui[k]/n+1,y=dui[k]%n+1;
37         for(int i=0;i<4;i++)if(visited[x+fx[i]][y+fy[i]]!=_s && a[x][y]>a[x+fx[i]][y+fy[i]]){
38             visited[x+fx[i]][y+fy[i]]=_s;
39             dui[++tou]=(x+fx[i]-1)*n+y+fy[i]-1;
40         }
41     }
42     for(int i=1;i<=n;i++)  if(visited[m][i]==_s){  fleft[_s]=i;
43         for(int j=i+1;j<=n+1;j++)  if(visited[m][j]!=_s){  fright[_s]=j-1;//注意这里如果枚举到n就有可能n也被覆盖了,然后fright就是0
44             break;
45         }
46         break;
47     }
48 }
49 int main(){
50     //freopen("ddd.in","r",stdin);
51     freopen("flow.in","r",stdin);
52     freopen("flow.out","w",stdout);
53     memset(f,10,sizeof(f));
54     cin>>m>>n;
55     for(int i=1;i<=m;i++)
56         for(int j=1;j<=n;j++)
57             a[i][j]=read();
58     for(int i=0;i<=m+1;i++)  a[i][0]=999999999,a[i][n+1]=999999999;
59     for(int i=0;i<=n+1;i++)  a[0][i]=999999999,a[m+1][i]=999999999;
60     if(ans=bfs1()){  cout<<((m==1) ? 1 : 0)<<endl<<ans<<endl;  return 0;}//去你大爷的特判,这里有一行的情况
61     for(int i=1;i<=n;i++)  bfs2(i);
62     f[0]=0;
63     for(int i=1;i<=n;i++)
64         for(int j=1;j<=n;j++)
65             if(i>=fleft[j] && i<=fright[j])
66                 f[i]=min(f[i],f[fleft[j]-1]+1);
67     cout<<1<<endl<<f[n]<<endl;
68     return 0;
69 }
View Code

 

转载于:https://www.cnblogs.com/JSL2018/p/5933741.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值