题意
题解
容易通过
D
F
S
DFS
DFS 求解在城市
(
0
,
j
)
(0,j)
(0,j) 处建蓄水厂的情况下,在
N
−
1
N-1
N−1 行可以建输水站的城市集合
S
(
0
,
j
)
S_{(0,j)}
S(0,j)。由于连续的水利设施需要满足高度的单调性,搜索的路径上不可能出现环,那么可以记忆化搜索。
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 代表
S
(
i
,
j
)
S_{(i,j)}
S(i,j),对集合进行二进制状态压缩,那么有递推式
d
p
[
i
]
[
j
]
=
2
j
[
i
=
N
−
1
]
⋃
d
p
[
k
]
[
l
]
,
H
[
i
]
[
j
]
>
H
[
k
]
[
l
]
,
(
i
,
j
)
与
(
k
,
l
)
有
公
共
边
dp[i][j]=2^{j[i=N-1]}\bigcup dp[k][l],H[i][j]>H[k][l],(i,j)与(k,l)有公共边
dp[i][j]=2j[i=N−1]⋃dp[k][l],H[i][j]>H[k][l],(i,j)与(k,l)有公共边 问题转化为从行
0
0
0 中选取一个模值最小的城市集合
A
A
A,满足在集合对应的城市建蓄水厂的情况下,行
N
−
1
N-1
N−1 的城市都可以建输水站,即
⋃
d
p
[
0
]
[
j
]
=
2
M
−
1
,
(
0
,
j
)
∈
A
\bigcup dp[0][j]=2^M-1,(0,j)\in A
⋃dp[0][j]=2M−1,(0,j)∈A 这类从决策集合中选取一个子集,在满足某些约束的条件下的最优化问题,形式与背包问题一致,可以考虑依次处理行
0
0
0 的每一座城市,以约束条件为状态进行转移。但问题在于约束条件的值域为
O
(
2
M
)
O(2^M)
O(2M),且每轮决策后最坏情况下状态空间翻倍,那么也无法对状态哈希并使用滚动数组。
容易观察到,若某个集合 S ( 0 , j ) S_{(0,j)} S(0,j) 在位置上不连续,考虑输水需要满足的单调性,那么被集合城市包围的城市不可能建输水站。则在满足行 N − 1 N-1 N−1 都可以建水利设施的情况下,集合 S ( 0 , j ) S_{(0,j)} S(0,j) 在位置上连续,那么可以简单地用连续区间的左右端点表示集合。
问题化简为已知城市 ( 0 , j ) (0,j) (0,j) 处建蓄水厂的情况下,在 N − 1 N-1 N−1 行可以建输水站的城市区间 [ l j , r j ) [l_j,r_j) [lj,rj),求可以覆盖行 N − 1 N-1 N−1 区间 [ 0 , M ) [0,M) [0,M) 的最小区间数量。这个问题不需要使用背包 D P DP DP 求解,而可以更简单地使用贪心策略,即从左端点 0 0 0 开始,依次选择左端点被当前覆盖区间包含或位于覆盖区间右侧相邻位置,且右端点值最大的区间。总时间复杂度 O ( N M ) O(NM) O(NM)。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 505, inf = 0x3f3f3f3f;
const int dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0};
struct node
{
int l, r;
node() : l(inf), r(-inf) {}
bool operator<(const node &o) const { return l < o.l; }
} mem[maxn][maxn], A[maxn];
int N, M, H[maxn][maxn];
bool used[maxn][maxn];
void rec(int x, int y)
{
if (used[x][y])
return;
used[x][y] = 1;
auto &res = mem[x][y];
for (int i = 0; i < 4; ++i)
{
int nx = x + dx[i], ny = y + dy[i];
if (0 <= nx && nx < N && 0 <= ny && ny < M && H[x][y] > H[nx][ny])
{
rec(nx, ny);
auto t = mem[nx][ny];
res.l = min(res.l, t.l), res.r = max(res.r, t.r);
}
}
if (x == N - 1)
res.l = min(res.l, y), res.r = max(res.r, y + 1);
}
int main()
{
cin >> N >> M;
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
cin >> H[i][j];
for (int i = 0; i < M; ++i)
rec(0, i), A[i] = mem[0][i];
int omit = 0;
for (int i = 0; i < M; ++i)
omit += mem[N - 1][i].l == inf;
if (omit > 0)
{
cout << 0 << '\n'
<< omit << '\n';
return 0;
}
sort(A, A + M);
int cur = 0, nxt = 0, res = 0;
for (int i = 0; i < M; ++i)
{
if (A[i].l == inf)
break;
nxt = max(nxt, A[i].r);
if ((i != M - 1 && A[i + 1].l > cur) || (i == M - 1 && cur < M))
++res, cur = nxt;
}
cout << 1 << '\n'
<< res << '\n';
return 0;
}