[洛谷P4001] [BJOI2006]狼抓兔子 --- 网络流-最小割

题目描述

现在小朋友们最喜欢的”喜羊羊与灰太狼”,话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形:

这里写图片描述

左上角点为(1,1),右下角点为(N,M)(上图中N=3,M=4).有以下三种类型的道路

1:(x,y)<==>(x+1,y)

2:(x,y)<==>(x,y+1)

3:(x,y)<==>(x+1,y+1)

道路上的权值表示这条路上最多能够通过的兔子数,道路是无向的. 左上角和右下角为兔子的两个窝,开始时所有的兔子都聚集在左上角(1,1)的窝里,现在它们要跑到右下角(N,M)的窝中去,狼王开始伏击这些兔子.当然为了保险起见,如果一条道路上最多通过的兔子数为K,狼王需要安排同样数量的K只狼,才能完全封锁这条道路,你需要帮助狼王安排一个伏击方案,使得在将兔子一网打尽的前提下,参与的狼的数量要最小。因为狼还要去找喜羊羊麻烦。

输入格式:
第一行为N,M.表示网格的大小,N,M均小于等于1000.

接下来分三部分

第一部分共N行,每行M-1个数,表示横向道路的权值.

第二部分共N-1行,每行M个数,表示纵向道路的权值.

第三部分共N-1行,每行M-1个数,表示斜向道路的权值.

输出格式:
输出一个整数,表示参与伏击的狼的最小数量.

输入样例:
3 4
5 6 4
4 3 1
7 5 3
5 6 7 8
8 7 6 5
5 5 5
6 6 6
输出样例:
14

分析

按题意,本题求该网络的最小割
根据最大流最小割定理可知:最小割 = 最大流,因此,直接跑网络流把,不过本题由于数据量较大,需要做出些优化,比如建边(相对蓝书的模板而言)

PS:本题还有更快的方法(对偶图+最短路),对此不做探究

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>

#define IL inline
#define INF 0x7f7f7f7f 

using namespace std;

inline int read()
{
    char c=getchar();
    int sum=0,k=1;
    for(;'0'>c || c>'9';c=getchar())
        if(c=='-') k=-1;
    for(;'0'<=c && c<='9';c=getchar()) sum=sum*10+c-'0';
    return sum*k;
}

int S, T;

struct Edge
{
    int to, nxt;
    int flow;
    Edge(int v = 0, int nxt_ = 0, int f = 0)
    {
        to = v; nxt = nxt_; flow = f;
    }
}edge[6000000];
int last[1000005];
int cnt;
IL void add(int u, int v, int f)
{
    edge[cnt] = Edge(v, last[u], f); last[u] = cnt++;
    edge[cnt] = Edge(u, last[v], f); last[v] = cnt++; // 此处可以理解为将原本要添加的四条边两两合并
}

int dep[1000005];
int cur[1000005];

IL int min_(int x, int y) { return x < y ? x : y ; }

IL bool Bfs()
{
    memset(dep, 0, sizeof(dep));
    queue<int>Q;
    dep[S] = 1;
    Q.push(S);
    for(int t = 1, u; t;)
    {
        u = Q.front(); Q.pop(); --t;
        for(int i = last[u]; i != -1; i = edge[i].nxt)
        if(!dep[edge[i].to] && edge[i].flow)
        {
            dep[edge[i].to] = dep[u] + 1;
            Q.push(edge[i].to); ++t;
        }
    }
    return dep[T];
}

IL int Dfs(int u, int a)
{
    if(u == T || !a) return a;
    int flow = 0, f;
    for(int &i = cur[u]; i != -1; i = edge[i].nxt)
    if(dep[edge[i].to] == dep[u] + 1 && edge[i].flow)
    if((f = Dfs(edge[i].to, min_(a, edge[i].flow))) > 0)
    {
        edge[i].flow -= f;
        edge[i ^ 1].flow += f;
        flow += f;
        if(!(a -= f)) return flow;
    }else
        dep[edge[i].to] = -1;
    return flow;
}

IL int maxflow()
{
    int flow = 0;

    for(; Bfs();)
    {
        for(int  i = S; i <= T; ++i) cur[i] = last[i];
        flow += Dfs(S, INF);
    }

    return flow;
}

int main()
{
    int n = read(), m = read(), w;
    S = 1; T = n * m;
    memset(last, -1, sizeof(last));

    for(int i = 1, k = 1; i <= n; ++i, ++k)
    for(int j = 1; j < m; ++j, ++k)
        add(k, k + 1, read());

    for(int i = 1, k = 1; i < n; ++i)
    for(int j = 1; j <= m; ++j, ++k)
        add(k, k + m, read());

    for(int i = 1, k = 1; i < n; k = i * m + 1, ++i)
    for(int j = 1; j < m; ++j, ++k)
        add(k, k + m + 1, read());

    printf("%d\n",maxflow());

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_27121257/article/details/79404755