码迷,mamicode.com
首页 > 其他好文 > 详细

P1514 引水入城

时间:2018-09-05 15:07:09      阅读:155      评论:0      收藏:0      [点我收藏+]

标签:思路   html   lin   证明   问题   记忆   代码   inline   getc   

不会区间覆盖,我真的菜!

这道题的第一个数字比较容易判断,从所有的沿海城市开始爆搜一遍,如果能全部搜到就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;
}

P1514 引水入城

标签:思路   html   lin   证明   问题   记忆   代码   inline   getc   

原文地址:https://www.cnblogs.com/Garen-Wang/p/9591783.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!