「NOIP 2010」引水入城
Published on 2016-11-07描述
在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个 行 列的矩形,其中每个格子都代表一座城市,每座城市都有一个海拔高度。
为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。因此,只有与湖泊毗邻的第 1 行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。
由于第 行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。
分析
首先用 BFS 来解决不能全部覆盖的情况。
现在必然存在一个选取方案,使得最后一行的城市被全部覆盖,我们考虑如何求解最少建造几个蓄水场。
定理一:从第一行的某个城市 输水能够到达最后一个行的城市集合 ,设最后一行的城市为 ,集合 一定是一个区间 。
证明:不失一般性,我们假设集合 是 ,因为区间 一定能够被覆盖,若假设第一行的城市集合 能覆盖 ,第一行的 到最后一行 的中的每条路径一定与城市 到 的路径有交点,而这就表明 可以到达 ,这就推出了矛盾。所以集合 必定是一个区间 。
根据定理一,我们求出第一行的每个城市能覆盖的区间 ,现在问题变成了选择最少的区间,使得 全部被覆盖,这是经典的区间覆盖问题,排序后贪心可解决。
复杂度:。
总结:本题需要得到第一行的城市覆盖最后一行的城市是一个区间,这样才能够转化为区间覆盖问题,否则就退化为集合覆盖问题,是没有多项式级别的解的。
吐槽:本题的精髓就在于证明定理一,如果不能证明它,那么这个题没有任何意义。所以我没有办法理解为什么有些人说:“我不会证明!”,这或许是对自己极不负责任的一种态度吧,做再多的题,如果不能抓住每个题的精髓,那么做的题又有什么意义?我替这些人感到悲哀。
代码
// Created by Sengxian on 2016/11/7. // Copyright (c) 2016年 Sengxian. All rights reserved. // Vijos 1777 BFS + 区间覆盖 #include <bits/stdc++.h> using namespace std; typedef long long ll; inline int ReadInt() { static int n, ch; n = 0, ch = getchar(); while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) n = n * 10 + ch - '0', ch = getchar(); return n; } const int MAX_N = 500 + 3, MAX_M = 500 + 3; int n, m, a[MAX_N][MAX_M]; bool vis[MAX_N][MAX_M]; struct Point { int x, y; Point(int x = 0, int y = 0): x(x), y(y) {} }; const int dx[] = {0, 0, 1, -1}; const int dy[] = {1, -1, 0, 0}; Point q[MAX_N * MAX_M]; void judge() { int l = 0, r = 0; memset(vis, 0, sizeof vis); for (int i = 0; i < m; ++i) vis[0][i] = true, q[r++] = Point(0, i); while (r - l >= 1) { Point now = q[l++]; for (int k = 0; k < 4; ++k) { Point ne = Point(now.x + dx[k], now.y + dy[k]); if (ne.x >= 0 && ne.x < n && ne.y >= 0 && ne.y < m && a[now.x][now.y] > a[ne.x][ne.y] && !vis[ne.x][ne.y]) { vis[ne.x][ne.y] = true; q[r++] = ne; } } } int cnt = 0; for (int i = 0; i < m; ++i) cnt += vis[n - 1][i]; if (cnt != m) { printf("0\n%d\n", m - cnt); exit(0); } } typedef pair<int, int> range; range Bfs(int sx, int sy) { int l = 0, r = 0; memset(vis, 0, sizeof vis); q[r++] = Point(sx, sy), vis[sx][sy] = true; while (r - l >= 1) { Point now = q[l++]; for (int k = 0; k < 4; ++k) { Point ne = Point(now.x + dx[k], now.y + dy[k]); if (ne.x >= 0 && ne.x < n && ne.y >= 0 && ne.y < m && a[now.x][now.y] > a[ne.x][ne.y] && !vis[ne.x][ne.y]) { vis[ne.x][ne.y] = true; q[r++] = ne; } } } int ll = m, rr = m; for (int i = m - 1; i >= 0; --i) if (vis[n - 1][i]) ll = i; for (int i = 0; i < m; ++i) if (vis[n - 1][i]) rr = i; return range(ll, rr + 1); } range ranges[MAX_M]; void solve() { for (int i = 0; i < m; ++i) ranges[i] = Bfs(0, i); sort(ranges, ranges + m); int i = 0, pos = 0, cnt = 0; while (pos < m) { int mx = -1; while (i < m && ranges[i].first <= pos) mx = max(mx, ranges[i++].second); assert(mx != -1); pos = mx, cnt++; } printf("1\n%d\n", cnt); } int main() { n = ReadInt(), m = ReadInt(); for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j) a[i][j] = ReadInt(); judge(); solve(); return 0; }