P1514 引水入城

P1514 引水入城

给定一张二维高度地图, 你可以在第一行的格子里建水库。 若是两个相邻格子高度差大于 \(1\), 那么水可以从高的流去低的
现在问能否使最后一行的每个格子里都有水, 若是能输出 \(1\) ,再输出最少建水库数; 若是不能输出不能的格子数


错误日志: 思路错了
调试日志: 区间覆盖未转换为闭区间


Solution

(反思)刚开始想从最后一行倒着搜上去, 用 \(bitset\) 维护一个01序列表示每个格子至少需要在第一行建哪些水库之一
过样例交了一发 \(40\)分。
然后发现这个东西不是单单取并就好了啊。。处理完之后是个 \(k-SAT\) 无多项式解的。。

然后想正解
有无解很好判断, 搜索一下就出来了, 不可行答案直接统计即可
讨论有解的情况
首先对于第一行的每个水库 \(x_{i}\) , 他能覆盖的最后一行必然是一个连续的区间
画图很好证明的
反证法:
假设存在一种情况使得覆盖情况如下:

假设蓝色覆盖路线如下:

因为右边红色被覆盖了, 所以从红色水库到下方必然有一条路径

发现路径必有交(紫色部分), 所以红色水库的水也会流入蓝色那部分, 假设不成立
故一座水库能覆盖的最后一行必然是一个连续的区间
证毕。

然后搜索一下第一行的每个格子能到达的区间
做区间最少线段覆盖即可

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
#define LL long long
#define REP(i, x, y) for(int i = (x);i <= (y);i++)
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 1019;
int lenx, leny;
int map[maxn][maxn];
bool vis[maxn][maxn];
int l[maxn][maxn], r[maxn][maxn];
int mx[4] = {0, 0,-1, 1};
int my[4] = {1,-1, 0, 0};
bool judge(int x, int y){return !(x < 1 || x > lenx || y < 1 || y > leny);}
void dfs(int x, int y){
    vis[x][y] = 1;//记忆化+到达标记
    REP(k, 0, 3){
        int nx = x + mx[k], ny = y + my[k];
        if(!judge(nx, ny) || map[nx][ny] >= map[x][y])continue;
        if(!vis[nx][ny])dfs(nx, ny);
        l[x][y] = min(l[x][y], l[nx][ny]);
        r[x][y] = max(r[x][y], r[nx][ny]);
        }
    }
struct Node{int l, r;}I[maxn];
int cnt;
int main(){
    lenx = RD(), leny = RD();
    REP(i, 1, lenx)REP(j, 1, leny)map[i][j] = RD();
    memset(l, 127, sizeof(l));//r初始为0就得
    REP(i, 1, leny)l[lenx][i] = r[lenx][i] = i;
    REP(i, 1, leny)dfs(1, i);
    int flag = 0;
    REP(i, 1, leny){
        if(!vis[lenx][i])flag++;
        if(r[1][i])I[++cnt] = (Node){l[1][i] - 1, r[1][i]};//记录合法线段(记得转换为区间相交)
        }
    if(flag){printf("0\n%d\n", flag);return 0;}
    int ans = 0, now = 0, next = 0;
    REP(i, 1, cnt){
        if(I[i].l <= now)next = max(next, I[i].r);
        else{
            ans++, now = next;
            if(I[i].l > now);//已经判断一定有解了
            next = max(next, I[i].r);
            }
        }
    puts("1");
    if(now < leny)printf("%d\n", ans + 1);
    else printf("%d\n", ans);
    return 0;
    }
posted @ 2018-10-29 17:40 Tony_Double_Sky 阅读(...) 评论(...) 编辑 收藏