hdu3251 最小割

http://acm.hdu.edu.cn/showproblem.php?pid=3251
题意:
给定一张图,可以选择一些点,每个点有对应的点权,每条边有边权,选择一部分点后,必须摧毁一部分边,保证 1 不能到达这些点,获得的收益就是选择的点权和减去摧毁的边权和。并求出摧毁哪些边。
做法:
将带权的点连接一条边到t点,容量为该点的点权。最后将s连到1,容量为inf。求最小割,利用总的点权和减去最小割就是获得的收益。因为如果这个点不选择的话,最小割肯定包含该点到t点的边。如果要选择的话,肯定不包含,必须包含之前的点。
求摧毁的边,就可以跑完最大流以后,用dfs求一遍,将图划分成两个集合,如果一条边的起点与s在同一个集合,终点与t在同一个集合那么这条就是割边,(并且要排除掉与t相连的那些割边)。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;
const int M_node = 4000,M_edge = 210009,INF = 0x3f3f3f3f;
struct edge
{
    int to,cap,next;
    int index;
}edge[M_edge];
int head[M_node],iter[M_node];
int level[M_node];
bool vis[M_node];
int num;
int n,m,f,s,t;
int sum;
void init()
{
    memset(head,-1,sizeof(head));
    memset(vis,false,sizeof(vis));
    num = 0;
    sum = 0;
}
void add_edge(int u,int v,int cap,int index)
{
    edge[num].to = v; edge[num].cap = cap ; edge[num].next = head[u] ; edge[num].index = index ; head[u] = num++;
    edge[num].to = u; edge[num].cap = 0 ; edge[num].next = head[v] ; edge[num].index = -1 ; head[v] = num++;
}
bool bfs(int s,int t)
{
    memset(level,-1,sizeof(level));
    level[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int v = q.front();
        q.pop();
        for(int i = head[v];i != -1;i = edge[i].next)
        {
            int u = edge[i].to;
            if(level[u] < 0 && edge[i].cap)
            {
                level[u] = level[v] + 1;
                q.push(u);
            }
        }
    }
    return level[t] != -1;
}
int dfs(int v,int t,int f)
{
    if(v == t) return f;
    for(int &i = iter[v];i != -1;i = edge[i].next)
    {
        int u = edge[i].to;
        if(level[u] > level[v] && edge[i].cap > 0)
        {
            int d = dfs(u,t,min(f,edge[i].cap));
            if(d > 0)
            {
                edge[i].cap -= d;
                edge[i^1].cap += d;
                return d;
            }
        }
    }
    level[v] = -1;
    return 0;
}
int max_flow(int s,int t)
{
    int flow = 0;
    while(bfs(s,t))
    {
        for(int i = 0;i <= t+10;i++) iter[i] = head[i];
        int f = 0;
        while((f = dfs(s,t,INF)) > 0) flow += f;
    }
    return flow;
}
void dfs(int u,int fa)
{
    for(int i = head[u];i != -1;i = edge[i].next)
    {
        int v = edge[i].to;
        if(!vis[v] && edge[i].cap > 0)
        {
            vis[v] = true;
            dfs(v,u);
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    int kas = 1;
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&f);
        init();
        s = 0;
        t = n+1;
        add_edge(s,1,INF,-1);
        for(int i = 0;i < m;i++)
        {
            int u,v,cap;
            scanf("%d%d%d",&u,&v,&cap);
            add_edge(u,v,cap,i+1);
        }
        for(int i = 0;i < f;i++)
        {
            int u,cost;
            scanf("%d%d",&u,&cost);
            add_edge(u,t,cost,-1);
            sum += cost;
        }
        int ans = max_flow(s,t);
        ans = sum - ans;
        printf("Case %d: %d\n",kas++,ans);
        vis[1] = true;
        dfs(1,-1);
        queue<int> q;
        for(int i = 0;i < num;i += 2) // += 2 保证是正向边
        {
            if(vis[edge[i^1].to] && !vis[edge[i].to] && edge[i].index != -1) q.push(edge[i].index);
        }
        printf("%d",q.size());
        while(!q.empty())
        {
            printf(" %d",q.front());
            q.pop();
        }
        printf("\n");
        /*set<int> s;
        for(int i = 1;i <= n;i++)
        {
            for(int j = head[i]; j != -1 ;j = edge[j].next)
            {
                int v = edge[j].to;
                if(vis[i] && !vis[v] && edge[j].index != -1) s.insert(edge[j].index); //必须保证是正向边
            }
        }
        printf("%d",s.size());
        set<int>::iterator it;
        for(it = s.begin();it != s.end();it++)
        {
            printf(" %d",*it);
        }
        printf("\n");*/
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值