题意比较绕,多看两遍就看懂了
重点在于求满足要求的情况时的答案
可能也是可想的
注意到对于一个第一行的点,如果是满足要求的情况下,
他所能到达的一定是一段连续的区间
考虑如果不连续的话,那一定是绕过了一个区域,
那这一圈都是可以流过去的,而且这一圈都不能流进这个区域
那他就是一个不满足要求的情况
所以对于一个点,他能给供水的是一段区间
那这就转化成了一个线段覆盖问题
正好之前也没做过,这里写一下:
有两种写法,
第一种是贪心:在当前已经选出的区间中找一条左端点在其中,
且右端点最大的线段,之后将这条线段并入已选区间
直至选完
第二种是 dp:f[i] 表示到第 i 个位置最少用 f[i] 条线段覆盖
转移显然是从一段的最小转移,由于要保证最小,
就用线段树维护区间最小就行了
转移的位置只要保证能跟之前的拼起来就行
由于比较懒就写了贪心。。。
需要注意的是贪心的话你要保证跟之前区间接起来的话,是已选区间右端点 + 1就行
代码:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <queue>
using namespace std;
const int MAXN = 505, MAXM = 505;
struct POS{
int x, y;
POS(int X = 0, int Y = 0) {x = X; y = Y;}
};
struct LINE{
int l, r;
LINE(int L = -1, int R = -1) {l = L; r = R;}
bool operator < (const LINE& b) const {
return ((l == b.l) ? (r > b.r) : (l < b.l));
}
inline void update(LINE val) {
if (~val.l) {
l = ((~l) ? min(l, val.l) : val.l);
r = max(r, val.r);
}
return;
}
}line[MAXN][MAXM], myb[MAXM];
int n, m, totok;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int mp[MAXN][MAXM];
bool vis[MAXN][MAXM];
queue<POS> q;
inline void chk() {
for (int i = 1; i <= m; ++i) {
q.push(POS(1, i));
vis[1][i] = true;
}
while (!q.empty()) {
POS cur = q.front(); q.pop();
for (int i = 0; i < 4; ++i) {
int tx = cur.x + dx[i], ty = cur.y + dy[i];
if (tx < 1 || tx > n || ty < 1 || ty > m || vis[tx][ty] || mp[cur.x][cur.y] <= mp[tx][ty]) continue;
vis[tx][ty] = true;
q.push(POS(tx, ty));
}
}
return;
}
LINE dfs(POS cur, POS frm) {
if (cur.x == n) {
line[cur.x][cur.y].update(LINE(cur.y, cur.y));
}
for (int i = 0; i < 4; ++i) {
int tx = cur.x + dx[i], ty = cur.y + dy[i];
if (tx < 1 || tx > n || ty < 1 || ty > m || (tx == frm.x && ty == frm.y) || mp[cur.x][cur.y] <= mp[tx][ty]) continue;
line[cur.x][cur.y].update(dfs(POS(tx, ty), cur));
}
return line[cur.x][cur.y];
}
inline void getans() {
for (int i = 1; i <= m; ++i)
myb[i] = line[1][i];
sort(myb + 1, myb + m + 1);
LINE curitv;
int ans = 1, ptr = 1;
while (!(~myb[ptr].l)) ++ptr;
curitv = myb[ptr];
++ptr;
while (curitv.r < m) {
register int curmax = 0;
while (ptr <= m && myb[ptr].l <= curitv.r + 1) {
curmax = max(curmax, myb[ptr].r);
++ptr;
}
curitv.r = curmax;
++ans;
}
printf("1
%d
", ans);
return;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
scanf("%d", &mp[i][j]);
chk();
bool GG = false;
for (int i = 1; i <= m; ++i)
if (!vis[n][i]) {
GG = true;
++totok;
}
if (GG) {
printf("0
%d
", totok);
return 0;
}
for (int i = 1; i <= m; ++i)
dfs(POS(1, i), POS(0, 0));
getans();
return 0;
}