[学习笔记] UVA 1659 Help Little Laura - 最大费用循环流 - 学习笔记

先边权取负变为最小费用循环流。
然后对于边<u,v,f,c>,如果c>=0,则还是连<u,v,f,c>。
否则建立源点汇点,连<s,v,f,0>,<v,t,f,0>,<v,u,f,-c>,并且ans+=c。
然后ans+最小费用最大流就是答案。
本质类似带下界费用流和最大权闭合子图,先钦定负权边都跑了,然后退最小费用的流使得流量平衡。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define db double
#define ull unsigned lint
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int INF=INT_MAX/2-10,N=110,M=100010;const db eps=1e-10;
inline int squ(int x) { return x*x; }
struct edges{
	int from,to,pre,resf;db wgt;
}e[M];int h[N],etop,inq[N],fr[N],x[N],y[N],frs[N];db d[N];queue<int> q;vector<int> to[N];
inline int add_edge(int u,int v,int f,db c,int x=0) { return e[x=++etop].from=u,e[x].to=v,e[x].pre=h[u],h[u]=x,e[x].wgt=c,e[x].resf=f; }
inline int build_edge(int u,int v,int f,db c) { return add_edge(u,v,f,c),add_edge(v,u,0,-c); }
inline bool spfa(int s,int t,int n,db &ans)
{
	while(!q.empty()) q.pop();
	rep(i,1,n) d[i]=INF,inq[i]=0,fr[i]=0;
	d[s]=0,q.push(s),inq[s]=1;
	while(!q.empty())
	{
		int x=q.front();q.pop(),inq[x]=0;
		for(int i=h[x],y;i;i=e[i].pre)
			if(e[i].resf&&d[y=e[i].to]>d[x]+e[i].wgt)
				d[y]=d[x]+e[i].wgt,fr[y]=i,(!inq[y]?q.push(y),inq[y]=1:0);
	}
	if(!fr[t]) return false;int minf=INF;
	for(int i=fr[t];i;i=fr[e[i].from])
		minf=min(minf,e[i].resf);
	for(int i=fr[t];i;i=fr[e[i].from])
		e[i].resf-=minf,e[((i-1)^1)+1].resf+=minf;
	return ans+=d[t]*minf,true;
}
inline db dinic(int s,int t,int n) { db ans=0.0;while(spfa(s,t,n,ans));return ans; }
int main()
{
//	freopen("data.in","r",stdin),freopen("std.out","w",stdout);
	for(int n,cas=1;(n=inn());cas++)
	{
		int X=inn(),Y=inn();
		rep(i,1,n)
		{
			x[i]=inn(),y[i]=inn(),to[i].clear();
			for(int t;(t=inn());to[i].pb(t));
		}
		db ans=0;int s=n+1,t=s+1;
		memset(h,0,sizeof(int)*(t+1)),etop=0;
		memset(frs,0,sizeof(int)*(t+1));
		rep(i,1,n) Rep(j,to[i])
		{
			int a=i,b=to[i][j];
			db d=Y-sqrt(squ(x[a]-x[b])+squ(y[a]-y[b]))*X;
			if(d>=0) build_edge(a,b,1,d);
			else frs[b]++,frs[a]--,build_edge(b,a,1,-d),ans+=d;
		}
		rep(i,1,n)
			if(frs[i]>0) build_edge(s,i,frs[i],0);
			else if(frs[i]<0) build_edge(i,t,-frs[i],0);
		ans+=dinic(s,t,t),printf("Case %d: %.2lf\n",cas,double(-ans+eps));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值