Gym 101128F Landscaping(网络流)题解

时间:2023-02-10 04:25:55

题意:n*m的地,从有高地和低地,从高地走到低地或者从低地走到高地花费a,把高地和低地互相改造一次花费b。现在要走遍每一行每一列,问最小花费

Gym 101128F Landscaping(网络流)题解

思路:超级源点连接所有低地,容量b;所有地向四周建边,容量a;高地连接超级汇点,容量b。假如sum(a) > b,那么流出b,即这个地改造;假如sum(a) < b,那么就不改造。

代码:

#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include <iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 50 * 50 + 10;
const int M = maxn * 30;
const ull seed = 131;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
struct Edge{
    int to, next, flow, cap;
}edge[maxn * 10];
int tot;
int head[maxn];
int gap[maxn], dep[maxn], pre[maxn], cur[maxn];
void init(){
    tot = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int rw = 0){
    edge[tot].to = v;
    edge[tot].cap = w;
    edge[tot].flow = 0;
    edge[tot].next = head[u];
    head[u] = tot++;

    edge[tot].to = u;
    edge[tot].cap = rw;
    edge[tot].flow = 0;
    edge[tot].next = head[v];
    head[v] = tot++;
}
int sap(int start, int end, int N){
    memset(gap, 0, sizeof(gap));
    memset(dep, 0, sizeof(dep));
    memcpy(cur, head, sizeof(head));
    int u = start;
    pre[u] = -1;
    gap[u] = N;
    int ans = 0;
    while(dep[start] < N){
        if(u == end){
            int Min = INF;
            for(int i = pre[u]; i != -1; i = pre[edge[i ^ 1].to]){
                Min = min(Min, edge[i].cap - edge[i].flow);
            }
            for(int i = pre[u]; i != -1; i = pre[edge[i ^ 1].to]){
                edge[i].flow += Min;
                edge[i ^ 1].flow -= Min;
            }
            u = start;
            ans += Min;
            continue;
        }
        bool flag = false;
        int v;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            v = edge[i].to;
            if(edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u]){
                flag = true;
                cur[u] = pre[v] = i;
                break;
            }
        }
        if(flag){
            u = v;
            continue;
        }
        int Min = N;
        for(int i = head[u]; i != -1; i = edge[i].next){
            if(edge[i].cap - edge[i].flow && dep[edge[i].to] < Min){
                Min = dep[edge[i].to];
                cur[u] = i;
            }
        }
        gap[dep[u]]--;
        if(!gap[dep[u]]) return ans;
        dep[u] = Min + 1;
        gap[dep[u]]++;
        if(u != start) u = edge[pre[u] ^ 1].to;
    }
    return ans;
}
char mp[maxn][maxn];
int n, m;
int getid(int x, int y){
    return (x - 1) * m + y;
}
int to[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
int main(){
    int a, b;
    scanf("%d%d", &n, &m);
    scanf("%d%d", &a, &b);
    int s = n * m + 1, e = n * m + 2;
    init();
    for(int i = 1; i <= n; i++){
        scanf("%s", mp[i] + 1);
        for(int j = 1; j <= m; j++){
            if(mp[i][j] == '.'){
                addEdge(s, getid(i, j), b);
            }
            else{
                addEdge(getid(i, j), e, b);
            }
        }
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            for(int k = 0; k < 4; k++){
                int x = i + to[k][0];
                int y = j + to[k][1];
                if(x < 1 || y < 1 || x > n || y > m) continue;
                addEdge(getid(i, j), getid(x, y), a);
            }
        }
    }
    int ans = sap(s, e, n * m + 2);
    printf("%d\n", ans);
    return 0;
}