HDU-6214 Smallest Minimum Cut(最小割)

84 篇文章 6 订阅
20 篇文章 0 订阅

2017 ACM/ICPC Asia Regional Qingdao Online

题意:

给定一个网络图,求该网络中割边数量最少的最小割集。

思路:

脑洞方法,在建图时将每条边的权值w改成w*(m+1)+1,最后求得的最大流除以(m+1)就是原图的最大流,模上(m+1)就是割边最少的最小割集的边数量。可以分析一下得知,(m+1)是为了保证模数在边数叠加之后仍然是模数,每条边权多+1正好符合了一条割边去代替多条割边的功能,想一下就明白了。。

还有一个过不了的算法,即在求得最大流之后,将所有满流的边权设为1,不满流的边权设为inf,然后跑一边最大流就是答案,但是却过不了所有数据。

但我认为这题本就是一道不能做的题目,再看下面的样例:


1
7 10
1 2
1 2 1
1 3 1
1 4 1
2 5 1
3 5 1
4 5 1
1 6 5
6 5 5
5 7 8
6 7 1000

对于第二种方法,跑最大流如果没用到1000这条边,那么跑出来的最小割的边数是2,而如果用到了这条边呢,最小的边数就是4。

但是第一种方法呢,跑出来的可能会是1和4(过的代码跑出1),但很明显这组样例的ans不可能是1的。

其实就是原题HDU-3987,但在这题两种方法都会过,狂套各种版本都不过真的很爆炸= =。

代码:

#include <algorithm>  
#include <iostream>  
#include <string.h>  
#include <vector>
#include <cstdio>  
#include <queue>  
using namespace std;  
const int inf = 0x3f3f3f3f;  
const int maxn = 205;  
const int maxm = 2005;  
struct node{int u, v, w, next;} edge[maxm];  
int dis[maxn], pre[maxn], rec[maxn], head[maxn], block[maxn];  
int n, m, no, s, t;  
queue<int> q;  
inline void init()  
{  
    no = 0;  
    memset(head, -1, sizeof head);  
}  
inline void add(int u, int v, int w)  
{  
    edge[no].u = u; edge[no].v = v; edge[no].w = w;  
    edge[no].next = head[u]; head[u] = no++;  
    edge[no].u = v; edge[no].v = u; edge[no].w = 0;  
    edge[no].next = head[v]; head[v] = no++;  
}  
void reset(int S, int T)  
{  
    memset(dis, 0x3f, sizeof dis);  
    memset(block, 0, sizeof block);  
    q.push(S); dis[S] = 0;  
    while(!q.empty())  
    {  
        int top = q.front(); q.pop();  
        for(int k = head[top]; k != -1; k = edge[k].next)  
        if(dis[edge[k].v] == inf && edge[k].w)  
            dis[edge[k].v] = dis[top]+1, q.push(edge[k].v);  
    }  
}  
int dinic(int S, int T)  
{  
    int ans = 0, flow = inf, top = S;  
    reset(S, T); pre[S] = S;  
    while(dis[T] != inf)  
    {  
        int k, tmp;  
        for(k = head[top]; k != -1; k = edge[k].next)  
        {  
            if(edge[k].w && dis[edge[k].v]==dis[top]+1 &&   
            !block[edge[k].v]) break;  
        }  
        if(k != -1)  
        {  
            tmp = edge[k].v;  
            flow = min(flow, edge[k].w);  
            pre[tmp] = top, rec[tmp] = k;  
            top = tmp;  
            if(top == T)  
            {  
                ans += flow; tmp = -1;  
                for(; top != S; top = pre[top])  
                {  
                    edge[rec[top]].w -= flow;  
                    edge[rec[top]^1].w += flow;  
                    if(!edge[rec[top]].w) tmp = top;  
                }  
                flow = inf;  
                if(tmp != -1)  
                {  
                    top = pre[tmp];  
                    for(; top != S; top = pre[top])  
                    flow = min(flow, edge[rec[top]].w);  
                    top = pre[tmp];  
                }  
            }  
        }  
        else  
        {  
            block[top] = 1;  
            top = pre[top];  
            if(block[S]) reset(S, T);  
        }  
    }  
    return ans;  
}  
void mapping()  
{  
    int u, v, w;
    for(int i = 1; i <= m; ++i)  
    {  
        scanf("%d %d %d", &u, &v, &w);  
        add(u, v, w*1001+1);  
    }  
}
int main()  
{  
    int tt; scanf("%d", &tt);
    while(tt--)  
    {
        scanf("%d %d", &n, &m);
        scanf("%d %d", &s, &t);
        init();  
        mapping();
        printf("%d\n", dinic(s, t)%1001);  
    }  
    return 0;  
}


继续加油~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值