HDU 1853 最小费用最大流

本文介绍如何使用最小费用流算法解决寻找最优环覆盖所有点的问题。通过拆点和特定建图策略,确保每个点仅被覆盖一次,并实现总花费最小化。

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

找出若干个环覆盖所有的点,使得总的花费最小

因为每个点只能经过一次,所以很快就可想到拆点求最小费用流

建图:

S->i  费用为0 流量为1

i+n->T同上

若有边u->v

u->v+n 费用为边权,容量为1

最后套套模板求一次最小费用流,如果流量等于n,表示每个点都遍历了一次,输出最小费用即可

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

const int n_max=505;
const int m_max=10005;
const int inf=1<<30;
int min_cost,max_flow;
int head[n_max];
struct edge
{
	int u,v,cap,cost,last;
	void add(int pu,int pv,int pcap,int pcost,int plast)
	{
		u=pu;
		v=pv;
		cap=pcap;
		cost=pcost;
		last=plast;
	}
}e[m_max<<2];
int ne;

void init()
{
	ne=0;
	memset(head,-1,sizeof(head));
}

void addedge(int u,int v,int cap,int cost)
{
	e[ne].add(u,v,cap,cost,head[u]);
	head[u]=ne++;
	e[ne].add(v,u,0,-cost,head[v]);
	head[v]=ne++;
}

void min_cost_max_flow(int s,int t)
{
	int d[n_max],p[n_max];
	bool inq[n_max];
	int id=0;
	min_cost=max_flow=0;
	while(true)
	{
		int i,u,v;
		for(i=0;i<n_max;i++)
		{
			d[i]=inf;
		}
		d[s]=0;
		memset(inq,0,sizeof(inq));
		memset(p,-1,sizeof(p));
		queue<int> q;
		q.push(s);
		inq[s]=true;
		while(!q.empty())
		{
			u=q.front();
			q.pop();
			inq[u]=false;
			for(i=head[u];i!=-1;i=e[i].last)
			{
				v=e[i].v;
				if(e[i].cap&&d[v]>d[u]+e[i].cost)
				{
					d[v]=d[u]+e[i].cost;
					p[v]=i;
					if(!inq[v])
					{
						q.push(v);
						inq[v]=true;
					}
				}
			}
		}
		if(d[t]==inf)
		{
			break;
		}
		int nmin=inf;
		for(u=p[t];u!=-1;u=p[e[u].u])
		{
			nmin=min(nmin,e[u].cap);
		}
		for(u=p[t];u!=-1;u=p[e[u].u])
		{
			e[u].cap -= nmin;
			e[u^1].cap += nmin;
		}
		max_flow+=nmin;
		min_cost+=d[t]*nmin;
	}
}

int main()
{
    int n,m,s,t;
    while(~scanf("%d%d",&n,&m))
    {
        s=0;
        t=n+n+1;
        init();
        int i,u,v,w;
        for(i=1;i<=n;i++)
        {
            addedge(s,i,1,0);
            addedge(n+i,t,1,0);
        }
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,n+v,1,w);
        }
        min_cost_max_flow(s,t);
        if(max_flow!=n)
        {
            printf("-1\n");
        }
        else
        {
            printf("%d\n",min_cost);
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值