题意:一个N*M矩形,每个格子有一个海拔。第一行靠近水源,要在矩形中恰当位置建水利设施将水引到最后一行的每个格子。有两种设施:抽水站,可以建在第一行任意位置;引水站,只要它周围存在一个格子比它地势高且那个格子建的有任意一种水利设施,就可以建造,建造后水引到这里。第一行输出1/0代表能否使得后一行全部引到水。如果是1,求最少需要多少抽水站;如果无法满足,输出(M - 最多可以满足的最后一行的城市数)。N,M<=500
题解:要使最后一行全部覆盖,再考察数据范围,可能的模型只有两种:二分图最小边覆盖集(ISAP),最少线段覆盖(贪心)。前者不好建图,先舍弃。考察后者,多画几个图,就可以发现第一行一个格子建抽水站使得最后一行可以获益的格子是连续的。如果不连续,则无解(因为其它点如果能到中间断开的那一段的话,那个点引出的路径和这个点引出的路径必有交点,则这个点引出的路径也可以沿这个交点走下去)。因此,一个第一行的格子对应在最后一行的一条连续线段,问题就转化为最少线段覆盖。
实现的时候要注意时间限制,可以先将所有第一行的点压入队列特判能否覆盖最后一行。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define code(x,y) (((x)-1)*M + y)
#define getx(s) ((s-1)/M + 1)
#define gety(s) ((s-1) % M + 1)
#define Max(a,b) ((a)>(b)?(a):(b))
const int dd[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
const int MAXN = 505;
int N, M;
int h[MAXN][MAXN];
bool ok[MAXN][MAXN];
bool dry[MAXN];
bool vis[MAXN][MAXN];
int Q[MAXN*MAXN];
void checkok()
{
int u, x, y, tx, ty, i;
int l = 1, r = 1;
memset(dry, 1, sizeof dry);
for (i = 1; i<=M; ++i) Q[r++] = code(1,i), vis[1][i] = 1;
while (l < r) {
u = Q[l++];
x = getx(u); y = gety(u);
if (x == N) dry[y] = 0;
for (i = 0; i<4; ++i) {
tx = x + dd[i][0];
ty = y + dd[i][1];
if (tx<1 || ty<1 || tx>N || ty>M) continue;
if (h[x][y] > h[tx][ty] && !vis[tx][ty])
Q[r++] = code(tx, ty), vis[tx][ty] = 1;
}
}
}
void BFS(int s)
{
int u, x, y, tx, ty, i;
int l = 1, r = 1;
Q[r++] = code(1, s);
memset(vis, 0, sizeof vis);
vis[1][s] = 1;
while (l < r) {
u = Q[l++];
x = getx(u); y = gety(u);
if (x == N) ok[s][y] = 1;
for (i = 0; i<4; ++i) {
tx = x + dd[i][0];
ty = y + dd[i][1];
if (tx<1 || ty<1 || tx>N || ty>M) continue;
if (h[x][y] > h[tx][ty] && !vis[tx][ty])
Q[r++] = code(tx, ty), vis[tx][ty] = 1;
}
}
}
struct Segment {
int b, e;
bool operator < (const Segment&t) const {
if (b==t.b) return e > t.e;
return b < t.b;
}
} a[MAXN];
void BuildSegs()
{
int i, j;
for (i = 1; i<=M; ++i)
for (j = 1; j<=M; ++j) {
if (ok[i][j] && !a[i].b) a[i].b = j;
if (ok[i][j]) a[i].e = j;
}
sort(a+1, a+M+1);
}
int MinCover()
{
int i = 1, last = 0, best, ans=0;
while (last < M) {
best = 0;
for (; a[i].b<=last+1 && i<=M; ++i)
best = Max(best, a[i].e);
last = best;
ans++;
}
return ans;
}
int main()
{
int i, j;
scanf("%d%d", &N, &M);
for (i = 1; i<=N; ++i)
for (j = 1; j<=M; ++j)
scanf("%d", &h[i][j]);
checkok();
int cnt = 0;
for (i = 1; i<=M; ++i)
if (dry[i]) ++cnt;
if (cnt) {
printf("0\n%d\n", cnt);
return 0;
}
for (i = 1; i<=M; ++i)
BFS(i);
BuildSegs();
printf("1\n%d\n", MinCover());
return 0;
}