noip 2010 引水入城(dfs + 贪心)

  非常感到抱歉,由于昨天有点忙,这道题的题解没写,今天补上,争取写的更加详细一点,让大家明白。

  看一下题意:

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

  为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。

  因此,只有与湖泊毗邻的第111 行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。由于第NNN 行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。

输入输出格式

  输入格式:

  每行两个数,之间用一个空格隔开。输入的第一行是两个正整数N,MN,MN,M,表示矩形的规模。接下来NNN 行,每行MMM 个正整数,依次代表每座城市的海拔高度。

   输出格式:

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

这次我把题意都拿了上来,洛谷有这道题,所以大家可以去洛谷等其他oj看题意以及测试数据,这里就不多说了,主要讲一讲思路。

  从这道题的数据范围来看,n、m<=500非常的大,所以大家需要考虑高效的算法,否则一不容易就会超时,这道题说了水能够从高处向地处流,然后求出最少建造的蓄水厂,

如果没有就输出‘0’......(还是看题意吧,题意说的非常清楚)。

  不难发现,我们必须要知道湖泊上的城市作为蓄水厂能够流到那里,这需要dfs进行求解,dfs也是需要技巧,需要记录当前位置能够输送到靠近沙漠的那些城市。

  这样我们就得到了湖泊城市与沙漠城市之间的关系,简化了问题,我们再看一看湖泊城市对应的沙漠城市,就发现对应的沙漠城市一定是连续的(想一想,为什么?)如果不连续

那么就没有解了,就需要输出不能灌溉城市的个数了,对于连续的城市,我们不难想到贪心中的区间覆盖问题,我们需要对区间进行排序,先排序左边(左边小的靠前),

左边相同看右边(右边大的靠前),这与区间选点的顺序不同(这很重要,否则不能得到最优解),然后按照区间覆盖的方案去做就行了,大家可以去看看其他博客上面区间

覆盖的解释,不难,然后与这道题对应下,关于区间覆盖的问题,我以后会说,下面就是代码:

// P1514 
// dfs + 贪心(区间覆盖问题)
#include <cstdio> 
#include <cstring> 
#include <algorithm>
using namespace std; 

const int maxn = 500 + 5;
int N, M, G[maxn][maxn]; 

int L[maxn][maxn], R[maxn][maxn]; // 其中L[1]表第1座靠近湖泊的城市对应的最左边的沙漠城市  
bool Vis[maxn][maxn], C[maxn];

const int dx[] = {-1, 1, 0, 0}; 
const int dy[] = {0, 0, -1, 1}; 

void dfs(int x, int y) {
  if (x == N-1) C[y] = 1; 
  for (int i = 0; i < 4; ++i) {
    int x1 = x + dx[i], y1 = y + dy[i]; 
    if (x1 >= 0 && x1 < N && y1 >= 0 && y1 < M && G[x][y] > G[x1][y1]) {
      if (Vis[x1][y1] == 0) {
        Vis[x1][y1] = 1; 
        dfs(x1, y1);
      }
      if (L[x][y] == -1 || (L[x][y] > L[x1][y1] && L[x1][y1] != -1)) L[x][y] = L[x1][y1]; 
      if (R[x][y] == -1 || (R[x][y] < R[x1][y1] && R[x1][y1] != -1)) R[x][y] = R[x1][y1]; 
    }
  }
}

int City[maxn];

bool cmp(int a, int b) {
  if (L[0][a] != L[0][b]) return L[0][a] < L[0][b]; 
  return R[0][a] > R[0][b];     
}

void find_pos(int& lastpos, int& rpos) {
  int larpos = 0, rem = rpos; 
  for (int i = rem; i < M; ++i) 
    if (L[0][City[i]] <= lastpos+1) {
      if (R[0][City[i]] > larpos) {
        larpos = R[0][City[i]]; 
        rpos = i;
      }
    }     
    else break; 
  lastpos = larpos; 
}

int main() { 
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout); 
  scanf("%d%d", &N, &M); 
  for (int i = 0; i < N; ++i) 
    for (int j = 0; j < M; ++j) 
      scanf("%d", &G[i][j]); 
  memset(L, -1, sizeof(L)); 
  memset(R, -1, sizeof(R));
  // dfs
  memset(Vis, 0, sizeof(Vis));
  for (int j = 0; j < M; ++j) L[N-1][j] = R[N-1][j] = j;
  for (int j = 0; j < M; ++j) if (!Vis[0][j]) {
    Vis[0][j] = 1; 
    dfs(0, j);  
  }
  // 贪心 区间覆盖问题  
  int cnt = 0; 
  for (int y = 0; y < M; ++y) if (C[y] == 0) ++cnt; 
  if (cnt) printf("0\n%d\n", cnt); 
  else {
    for (int y = 0; y < M; ++y) City[y] = y; 
    sort(City, City+M, cmp); 
    cnt = 0; 
    int pos = 0, rpos = 0; 
    while (pos != M-1) {
      cnt++; 
      find_pos(pos, rpos); 
    }
    printf("1\n%d\n", cnt);
  }
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/yifeiWa/p/11220241.html