bzoj-3130 费用流

141 篇文章 0 订阅
44 篇文章 0 订阅
题意:

给出一个运输网络,求该运输网络的最大流;

并且将运输网络的任意边赋上任意的单位流量权值v[i];

使∑v[i]=P,求一个最大流能使总费用最小;

n<=100,m<=1000;


题解:

第一问裸上Dinic;

第二问首先可以看出Bob一定会将所有的权值都赋给最大流量的边;

那么我们经应当使最大流量边最小;

然后二分。。。

二分的时候流量是实数。。实数网络流并不稳妥嘛;

要理性,不要愉悦!因为这道题精度限制不是十分精细,所以我们将所有的东西乘100000左右然后跑整数网络流;

然后我就被卡了。。从此我的Dinic模板多了一句话。。。

后面的点都出现了一种东西,就是一个二分完全图恰好被分为相邻两层,然后还没有流量。。。

之后每次都搜到它们都良妃一坨时间,所以我们要将不能流的点在这次DFS中删除;

if(ret==0) dis[x]=0;

然后就跑的飞快的AC了;


代码:


#include<queue>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110
#define M 11000
using namespace std;
typedef long long ll;
const ll K=100000;
int to[M<<1],next[M<<1],head[N],ce=1;
int val[M<<1];
ll flow[M<<1];
int n,dis[N];
queue<int>q;
void add(int x,int y,int v)
{
	to[++ce]=y;
	val[ce]=v;
	next[ce]=head[x];
	head[x]=ce;
	to[++ce]=x;
	val[ce]=0;
	next[ce]=head[y];
	head[y]=ce;
}
void clear(ll lim)
{
	for(int i=2;i<=ce;i+=2)
	{
		flow[i]=min(lim,K*val[i]);
		flow[i^1]=0;
	}
}
bool BFS()
{
	memset(dis,0,sizeof(dis));
	dis[1]=1;
	q.push(1);
	int x,i;
	while(!q.empty())
	{
		x=q.front(),q.pop();
		for(i=head[x];i;i=next[i])
		{
			if(flow[i]&&!dis[to[i]])
			{
				dis[to[i]]=dis[x]+1;
				q.push(to[i]);
			}
		}
	}
	return dis[n]!=0;
}
ll dfs(int x,ll ma)
{
	if(x==n)
	return ma;
	ll ret=0,temp;
	for(int i=head[x];i;i=next[i])
	{
		if(flow[i]&&dis[to[i]]==dis[x]+1)
		{
			temp=dfs(to[i],min(ma-ret,flow[i]));
			flow[i]-=temp,flow[i^1]+=temp;
			ret+=temp;
			if(ma==ret)
				return ret;
		}
	}
	if(!ret)	dis[x]=0;
	return ret;
}
ll Dinic(ll lim)
{
	clear(lim);
	ll ans=0;
	while(BFS())
		ans+=dfs(1,0x3f3f3f3f3f3f3f3fll);
	return ans;
}
int main()
{
	int m,p,i,j,k,x,y,v,ma;
	ll l,r,mid,F;
	scanf("%d%d%d",&n,&m,&p);
	for(i=1,ma=0;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&v);
		add(x,y,v);
		ma=max(ma,v);
	}
	F=Dinic(K*ma);
	printf("%lld\n",F/K);
	l=0,r=K*ma;
	while(l<=r)
	{
		mid=(l+r)/2;
		if(F==Dinic(mid))
			r=mid-1;
		else
			l=mid+1;
	}
	printf("%.4lf\n",(double)l/K*p);
	return 0;
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值