【SCOI2007】修车(费用流)

本文深入探讨了最小费用最大流算法的实现细节,通过具体的代码示例,讲解了如何利用该算法解决工人分配问题,优化成本并提高效率。文章详细介绍了算法的SPFA和DFS部分,展示了算法的具体应用。

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

好像大家似乎都在筹备退役滚粗文化课的后事了,我是不是也应该准备了??不过我不想退役啊55555

---------------------------------------------------------------------

很好,这个记性~=鱼,zkw板子还是背不熟,我**。

---------------------------------------------------------------------

这道题看似很狗,不过只要我们能想到将一个工人对于每辆车拆成n份来看的话就会简便许多,这样的话,等待时间就被我们具体化了,不再是抽象的,因此把N辆车和M*N个工人全部连起来,费用是编号*时间,表示需要等待的时间,并且S到车流1费0,工人到T流1费0。

#include<bits/stdc++.h>
const int N=1000;
const int INF=0x3f3f3f3f;
using namespace std;
int m,n,s,t,tot=1,first[N];
struct Edge
{
	int to,next,cap,f;
}edge[N*2];
inline void addedge(int from,int to,int c,int f)
{
	tot++;
	edge[tot].to=to; edge[tot].next=first[from]; edge[tot].cap=c; edge[tot].f=f; first[from]=tot;
}
inline void Add(int from,int to,int c,int f)
{
	addedge(from,to,c,f);
	addedge(to,from,0,-f);
}
bool inque[N],V[N];
int dis[N],ans=0;
inline bool spfa()
{
	memset(inque,0,sizeof(inque));
	memset(dis,0x3f,sizeof(dis));
	dis[t]=0; inque[t]=1;
	deque<int> q;	q.push_back(t);
	while(!q.empty())
	{
		int now=q.front(); q.pop_front();
		inque[now]=0;
		for(int u=first[now];u;u=edge[u].next)
		{
			int vis=edge[u].to;
			if(edge[u^1].cap&&dis[now]-edge[u].f<dis[vis])
			{
				dis[vis]=dis[now]-edge[u].f;
				if(!inque[vis])
				{
					inque[vis]=1;
					if(!q.empty()&&dis[vis]<dis[q.front()])	q.push_front(vis);
					else q.push_back(vis);
				}
			}
		}
	} 
	return dis[s]<INF;
}
int dfs(int now,int flow)
{
	V[now]=1;
	if(now==t)	return flow;
	int out=0;
	for(int u=first[now];u;u=edge[u].next)
	{
		int vis=edge[u].to;
		if(!V[vis]&&edge[u].cap&&dis[now]-edge[u].f==dis[vis])
		{
			int w=dfs(vis,min(edge[u].cap,flow));
			if(!w)	continue;
			ans+=w*edge[u].f; edge[u].cap-=w; edge[u^1].cap+=w;  out+=w; flow-=w;
			if(!flow)	break;
		}
	}
	return out;
}
inline void zkw()
{
	int ans=0;
	while(spfa())
	{
		V[t]=1;
		while(V[t])
		{
			memset(V,0,sizeof(V));
			ans+=dfs(s,INF);
		}
	}
}
int main()
{
	cin>>m>>n;
	s=0; t=n+m*n+1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			int t;
			cin>>t;
			for(int k=1;k<=n;k++)
				Add(i,n+(j-1)*n+k,1,k*t);
		}
	for(int i=1;i<=n;i++) Add(s,i,1,0);
	for(int i=1;i<=m*n;i++)	Add(n+i,t,1,0);
	zkw();
	cout<<fixed<<setprecision(2)<<((double)ans/(double)n)<<endl;
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值