P1514 [NOIP 2010 提高组] 引水入城题解
题目传送门
更好的阅读体验?
题目大意
一个国家可以看做是一个
思路分析
对于每一个在第
但是如果每一座城市都能收到水,至少该建几个蓄水厂呢?
定理
在有解的情况下,每个蓄水厂流到的第
证明
假设图上绿色的城市建造蓄水厂,水从蓝色的路线流向最后一行的黄色城市。其中橙色城市没有收到水。此时,第
因为要保证题目一定有解,所以一定有第
此时,绿色城市的供水路线与红色城市的供水路线存在着重叠,即图中深蓝色路线。而显然地,既然红色城市供的水能从深蓝色路线流向橙色城市,那么绿色城市供的水也可以。
因此,每个蓄水厂流到的第
证毕。
既然每个蓄水厂流到的第
思路很简单,采用贪心,先设一个变量记录最大左端点,遍历每条线段的左端点与右端点。在左端点小于等于最大左端点的线段中选择右端点最大的一条线段,将最大左端点更新为这条线段的右端点,以此往复,直到最大左端点大于等于
AC 代码
#include <bits/stdc++.h>
using namespace std;
struct city{ //蓄水厂,有水流向沙漠城市的左端点和右端点
int st,en;
}cities[510];
int n,m,s[510][510],L=1,ans=0;
int dh[5]={0,-1,0,1,0};
int dl[5]={0,0,1,0,-1};
bool f[510][510][510],p[510];
void bfs(int r,int c,int j){ //广搜,搜索水能流向那几座城市
queue<int> qx,qy;
qx.push(r);
qy.push(c);
f[j][r][c]=true;
if(n==1){
p[c]=true;
}
while(!qx.empty()){
int x=qx.front(),y=qy.front();
qx.pop();
qy.pop();
for(int i=1;i<=4;i++){
int dx=x+dh[i],dy=y+dl[i];
if(dx>=1 && dx<=n && dy>=1 && dy<=m){
if(!f[j][dx][dy] && s[dx][dy]<s[x][y]){
f[j][dx][dy]=true;
qx.push(dx);
qy.push(dy);
if(dx==n){
p[dy]=true; //标记数组,记录是不是每座沙漠城市都有水
cities[j].st=min(cities[j].st,dy); //因为一定是一个区间,所以直接统计左右端点
cities[j].en=max(cities[j].en,dy);
}
}
}
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>s[i][j];
}
}
for(int i=1;i<=m;i++){
cities[i].st=i;
cities[i].en=i;
}
for(int i=1;i<=m;i++){
bfs(1,i,i);
}
int city_num=0;
for(int i=1;i<=m;i++){
if(!p[i]){
city_num++;
}
}
if(city_num){ //有沙漠城市没有水
cout<<"0\n"<<city_num;
return 0;
}
cout<<"1\n";
while(L<=m){ //线段覆盖
int maxa=-1,maxn=-1e9;
for(int i=1;i<=m;i++){
if(cities[i].st<=L){
if(cities[i].en>maxn){
maxa=i;
maxn=cities[i].en;
}
}
}
L=cities[maxa].en+1;
ans++;
}
cout<<ans; //输出答案
return 0;
}