P1514 引水入城

中文题

想法:

首先需要一个结论: 如果有解,我们每个点覆盖的城市(线段)一定是连续的。因为如果不是连续,我么们可以很容易的证明这个点无法到达(它所在联通块边界一定高于相邻点)

所以我们利用记忆话搜索的方式预处理出所有点可以到达沙漠的一个连续区间的 L 和 R 

那么这个问题就变成了最小线段覆盖问题了

#pragma GCC optimize(3,"Ofast","inline")//O3优化
#pragma GCC optimize(2)//O2优化
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>
#include <bitset>
#include <cmath>
#include <sstream>
#include <iostream>
#include <cstring>

#define LL long long
#define ls nod<<1
#define rs (nod<<1)+1
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define INF 0x3f3f3f3f
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)

const double eps = 1e-10;
const int maxn = 500 + 10;
const LL mod = 998244353;

int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;}
using namespace std;

int mpp[maxn][maxn];
int vis[maxn][maxn];
int l[maxn][maxn],r[maxn][maxn];
int n,m;
int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};

void dfs(int x,int y) {
    vis[x][y] = 1;
    for (int i = 0; i < 4; i++) {
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if (xx < 1 || xx > n || yy < 1 || yy > m || mpp[xx][yy] >= mpp[x][y]) {
            continue;
        }
        if (!vis[xx][yy]) {
            dfs(xx, yy);
        }
        l[x][y] = min(l[x][y], l[xx][yy]);
        r[x][y] = max(r[x][y], r[xx][yy]);
    }
}


int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m;
    memset(l,INF, sizeof(l));
    memset(r,0, sizeof(r));
    for (int i = 1;i <= n;i++) {
        for (int j = 1;j <= m;j++) {
            cin >> mpp[i][j];
            l[n][j] = r[n][j] = j;
        }
    }
    for (int i = 1;i <= m;i++) {
        if (!vis[1][i])
            dfs(1,i);
    }
    bool fl = false;
    int cnt = 0;
    for (int i = 1;i <= m;i++) {
        if (!vis[n][i]) {
            fl = true;
            cnt++;
        }
    }
    if (fl) {
        cout << 0 << endl;
        cout << cnt << endl;
        return 0;
    }
    int lef = 1;
    while (lef <= m) {
        int maxr = 0;
        for (int i = 1;i <= m;i++) {
            if (l[1][i] <= lef)
                maxr = max(maxr,r[1][i]);
        }
        cnt++;
        lef = maxr + 1;
    }
    cout << 1 << endl;
    cout << cnt << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/-Ackerman/p/12554503.html