P1514 引水入城
不会区间覆盖,我真的菜!
这道题的第一个数字比较容易判断,从所有的沿海城市开始爆搜一遍,如果能全部搜到就1,否则0。
问题在于第二问。
一个可以证明的结论:当这个问题已经有解,每一个点向下面的提供水的范围是一个区间,不可能中间断了。
这个显然:如果中间断了的话,这个点就肯定无法到达,属于无解。
爆搜完了之后就可以想到一个神奇的思路:用最少的线段把\([1,m]\)这一段全都覆盖掉。
所以就是一个完全覆盖问题。请参考https://www.cnblogs.com/Draymonder/p/7215230.html
话说luogu上面普通提交,有的答案会RE,开O2就AC了。这氧气有毒。。。
然后会发现只有80分。
哪里可以优化?是我们的爆搜太慢了。
在从一个点为起点的爆搜中,我们可能经过一个点多次。但经过一个点多次并没有什么意义,所以干脆可以不访问。
这就是记忆化搜索的思路。
所以对于每一次令一个点为起点的dfs中,我们重置一下vis数组,搞一下记忆化搜索。
然后就可以愉快的过了。。。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 505;
const int INF = 0x3f3f3f3f;
int G[maxn][maxn];
int n, m;
int ll[maxn], rr[maxn];
bool vis[maxn][maxn];
bool vi[maxn];
struct Nodes
{
int l, r;
} s[maxn];
int tot;
bool cmp(const Nodes a, const Nodes b)
{
if(a.l == b.l) return a.r < b.r;
return a.l < b.l;
}
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
return s * ans;
}
void dfs(int x, int y, int t)
{
vis[x][y] = true;
if(x == n)
{
vi[y] = true;
ll[t] = std::min(ll[t], y);
rr[t] = std::max(rr[t], y);
}
if(x < n && G[x + 1][y] < G[x][y] && !vis[x + 1][y]) dfs(x + 1, y, t);
if(x > 1 && G[x - 1][y] < G[x][y] && !vis[x - 1][y]) dfs(x - 1, y, t);
if(y < m && G[x][y + 1] < G[x][y] && !vis[x][y + 1]) dfs(x, y + 1, t);
if(y > 1 && G[x][y - 1] < G[x][y] && !vis[x][y - 1]) dfs(x, y - 1, t);
}
int main()
{
n = read(), m = read();
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) G[i][j] = read();
memset(ll, 0x3f, sizeof ll);
memset(rr, -1, sizeof rr);
for(int i = 1; i <= m; i++)
{
dfs(1, i, i);
memset(vis, false, sizeof vis);
}
int cnt = 0;
for(int i = 1; i <= m; i++) if(vi[i]) cnt++;
if(cnt < m)
{
printf("0\n%d\n", n - cnt);
return 0;
}
for(int i = 1; i <= m; i++) if(ll[i] != INF && rr[i] != -1) s[++tot] = (Nodes){ll[i], rr[i]};
std::sort(s + 1, s + tot + 1, cmp);
//for(int i = 1; i <= tot; i++) printf("l: %d, r: %d\n", s[i].l, s[i].r);
//完全区间覆盖问题
cnt = 0;
int r = 1, i = 1;
while(r <= m)
{
int temp = 0;
while(s[i].l <= r)
{
temp = std::max(temp, s[i].r);
i++;
}
cnt++;
r = temp + 1;
}
printf("1\n%d\n", cnt);
return 0;
}