hdu 3251 Being a Hero(最小割+割边集输出)

本文通过一个实例详细解析了如何利用最小割方法解决特定问题。首先介绍了最小割的概念,然后阐述了一个涉及两个城市集合的问题,要求以最小成本使两个集合互不连通。文章通过构建图并应用最大流算法求解最小割,最终得出解决方案,并展示了如何输出这些解决方案。此案例有助于加深对最小割算法的理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接
分析:感觉对最小割还是不是很理解,这个题因为标签就是最小割,所以直接用最小割去想了,能做出来,如果不看标签还真不一定。
首先题目中很明显有两个集合,一个是你的城市,一个是国王的城市(不是你的城市的都是国王的城市)。而题目要求你做什么呢,就是用最小的花费让这两个集合互不连通,当然你也可以选择将你的城市中的一些城市让给国王,同样你也不再获得这个城市所带来的收益,这不免让人联想到“割”这个概念,由于你的城市可以转换成国王的城市,也就是你可以抛弃掉某个城市,但这样需要一定的花费,所以我们引入汇点T,规定与T相连的都是你的城市(假设初始时f个城市都是你的),连一条容量为结点权值的边,表示必须把这条边切断才能让这个结点转化成国王的城市,同样,如果不切掉,那么就要想办法去掉和他直接或间接相连的边。
所以我们建图时这样建,让f个城市想T连容量为结点权值的弧,对给的边连一条容量为边权的弧,最后从1到T求一个最小割,价值总和减去最小割就是答案。最后输出最小割中的边即是方案。

#include<bits/stdc++.h>
#define MAIN main
#define PII pair<int,int>
#define x first
#define y second
#define lk (k<<1)
#define rk (k<<1|1)
using namespace std;
typedef long long ll;
const double eps=1e-8;
const double pi=acos(-1.0);
const int INF=0x3f3f3f3f;
const int N=1e3+10;
struct edge
{
    int u,v,cap,flow,num;
    edge(int u=0,int v=0,int cap=0,int flow=0,int num=0):u(u),v(v),cap(cap),flow(flow),num(num){}
};
vector<edge>E;
vector<int>g[N];
void add(int u,int v,int cap,int num)
{
    E.push_back(edge(u,v,cap,0,num));
    E.push_back(edge(v,u,0,0,num));
    int m=E.size();
    g[u].push_back(m-2);
    g[v].push_back(m-1);
}
int vis[N],d[N],cur[N],n,m;
int bfs(int s,int t)
{
    queue<int>q;
    memset(vis,0,sizeof(vis));
    d[s]=0;vis[s]=1;q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<(int)g[u].size();i++){
            edge &e=E[g[u][i]];
            int v=e.v;
            if(!vis[v]&&e.cap>e.flow){
                vis[v]=1;
                q.push(v);
                d[v]=d[u]+1;
            }
        }
    }
    return vis[t];
}
int dfs(int s,int t,int a)
{
    if(s==t||a==0) return a;
    int flow=0,f;
    for(int &i=cur[s];i<(int)g[s].size();i++){
        edge &e=E[g[s][i]];
        int v=e.v;
        if(d[v]==d[s]+1&&(f=dfs(v,t,min(a,e.cap-e.flow)))>0){
            flow+=f;
            e.flow+=f;
            E[g[s][i]^1].flow-=f;
            a-=f;
            if(a==0) break;
        }
    }
    return flow;
}
int max_flow(int s,int t)
{
    int flow=0;
    while(bfs(s,t))
    {
        memset(cur,0,sizeof(cur));
        flow+=dfs(s,t,INF);
    }
    return flow;
}
int cost[N];
vector<int>res;
void solve()
{
    for(int i=1;i<=n;i++){
        if(!vis[i]) continue;
        for(int j=0;j<(int)g[i].size();j++){
            edge &e=E[g[i][j]];
            if(e.num&&e.cap&&!vis[e.v]&&e.cap==e.flow) res.push_back(e.num);
        }
    }
}
int MAIN()
{
    int test,tt=0,flag=0;
    scanf("%d",&test);
    while(test--)
    {
        if(flag) printf("\n");
        flag=1;
        int f;
        scanf("%d%d%d",&n,&m,&f);
        int s=1,t=n+1;
        E.clear();
        res.clear();
        for(int i=s;i<=t;i++) g[i].clear();
        memset(cost,0,sizeof(cost));
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w,i);
        }
        for(int i=1;i<=f;i++){
            int u,w;
            scanf("%d%d",&u,&w);
            cost[u]=w;
        }
        int ans=0;
        for(int i=2;i<=n;i++){
            if(cost[i]) {
                add(i,t,cost[i],0);
                ans+=cost[i];
            }
        }
        ans-=max_flow(s,t);
        printf("Case %d: %d\n",++tt,ans);
        solve();
        printf("%d ",(int)res.size());
        for(int i=0;i<(int)res.size();i++){
            printf("%d",res[i]);
            if(i!=(int)res.size()-1) printf(" ");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值