noip2010 引水入城 (bfs染色+贪心)

P1777引水入城

描述

在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个N行M列的矩形,其中每个格子都代表一座城市,每座城市都有一个海拔高度。

为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。因此,只有与湖泊毗邻的第1行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。

由于第N行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。

格式

输入格式

输入文件的每行中两个数之间用一个空格隔开。

输入的第一行是两个正整数N和M,表示矩形的规模。

接下来N行,每行M个正整数,依次代表每座城市的海拔高度。

输出格式

输出有两行。如果能满足要求,输出的第一行是整数1,第二行是一个整数,代表最少建造几个蓄水厂;如果不能满足要求,输出的第一行是整数0,第二行是一个整数,代表有几座干旱区中的城市不可能建有水利设施。

样例1

样例输入1[复制]

2 5
9 1 5 4 3
8 7 6 1 2

样例输出1[复制]

1
1

限制

每个测试点1s

提示

本题共有10个测试数据,每个数据的范围如下表所示:
测试数据编号 能否满足要求 N M
1 不能 ≤ 10 ≤ 10
2 不能 ≤ 100 ≤ 100
3 不能 ≤ 500 ≤ 500
4 能 = 1 ≤ 10
5 能 ≤ 10 ≤ 10
6 能 ≤ 100 ≤ 20
7 能 ≤ 100 ≤ 50
8 能 ≤ 100 ≤ 100
9 能 ≤ 200 ≤ 200
10 能 ≤ 500 ≤ 500
对于所有的10个数据,每座城市的海拔高度都不超过10^6。

来源

noip2010提高组复赛 

解析:1.从上往下进行染色,然后统计第 n 行未被染色的数量 s ,若s>0,则输出 0 s;

              为什么选择从上往下染色,而不是下方的从下往上染色,还能生一次搜索呢。(我买个关子,自己做的时候就知道了。)

           2.从下往上进行两次染色,第一次,从左往右进行染色,得到每一个第一行的点所能覆盖的左界;

                                                      第二次,从右往左进行染色,得到每一个第一行的点所能覆盖的右界。

          3.贪心。假设当前覆盖区间为[l,r],枚举第一行的点,从中选取能与当前区间[l,r]连接起来,并且向右延伸最远的点,作为下一个蓄水场建立点。   

代码:

#include<cstdio>
#include<algorithm>
#define maxn 500
using namespace std;

int n,m,h[maxn+20][maxn+20];
int color[maxn+20][maxn+20];
struct node{int x,y;}q[maxn*maxn+20];
struct tnode{int left,right;}p[maxn+20];
int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0};

bool cmp(tnode a,tnode b)
{
  if(a.left<b.left)return 1;
  if(a.left>b.left)return 0;
  return a.right>b.right;
}

void bfs_1(int a,int b)
{
  int i,j,k,l=0,r=0;
  q[0].x=a,q[0].y=b;
  
  while(l<=r)
    {
      for(k=0;k<4;k++)
        {
          i=q[l].x+dx[k],j=q[l].y+dy[k];
          if(i>=1 && i<=n && j>=1 && j<=m && 
		     color[i][j]!=-1 && h[i][j]<h[q[l].x][q[l].y])
		    q[++r].x=i,q[r].y=j,color[i][j]=-1;
        }
      l++;
    }
}

void bfs_2(int a,int b,int c)
{
  int i,j,k,l=0,r=0;
  q[0].x=a,q[0].y=b;
  
  while(l<=r)
    {
      for(k=0;k<4;k++)
        {
          i=q[l].x+dx[k],j=q[l].y+dy[k];
          if(i>=1 && i<=n && j>=1 && j<=m && 
		     color[i][j]<c && h[i][j]>h[q[l].x][q[l].y])
		    q[++r].x=i,q[r].y=j,color[i][j]=color[a][b];
        }
      l++;
    }
}

int main()
{
  int i,j,k,s,l,r;
  scanf("%d%d",&n,&m);
  for(i=1;i<=n;i++)
    for(j=1;j<=m;j++)
      scanf("%d",&h[i][j]);
  
  for(i=1;i<=m;i++)color[1][i]=-1;
  for(i=1;i<=m;i++)bfs_1(1,i);
  for(s=0,i=1;i<=m;i++)if(color[n][i]!=-1)s++;
  if(s>0)
    {
      printf("%d\n%d\n",0,s);
      return 0;
    }
  
  for(i=1;i<=m;i++)p[i].left=p[i].right=0;
  
  for(i=1;i<=m;i++)
    if(color[n][i]<1)color[n][i]=i,bfs_2(n,i,1);
  for(i=1;i<=m;i++)
    if(color[1][i]>=1 && color[1][i]<=m)p[i].left=color[1][i];
  
  for(i=m;i>=1;i--)
    if(color[n][i]<m+1)color[n][i]=m+i,bfs_2(n,i,m+1);
  for(i=1;i<=m;i++)
    if(color[1][i]>=m+1 && color[1][i]<=m+m)p[i].right=color[1][i]-m;

  s=0,r=0;
  while(r<m)
    {
      for(k=0,i=1;i<=m;i++)
        if(p[i].left==r+1)k=max(k,p[i].right);
      r=k,s++;
      
      for(i=1;i<=m;i++)
        if(p[i].right>r && p[i].left<=r)p[i].left=r+1;
    }
      
  printf("%d\n%d\n",1,s);
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值