bzoj1834(网络流+费用流)

给定一张有向图,每条边都有一个容量C和一个扩容费用W。这里扩容费用是指将容量扩大1所需的费用。求: 1、 在不扩容的情况下,1到N的最大流; 2、 将1到N的最大流增加K所需的最小扩容费用。


第一问裸的网络流

第二问:新建一个汇点,将n号点与汇点相连,容量为k,(限制最多增大的流量)费用为0;

将原先每一条边都新建一个边,容量为inf,表示可以任意大的扩建,费用的该边扩展所需的费用,如果原先的那一条边没有满,那么费用流时肯定是走那条边

如果满了,就可能走扩展的边,同时cost一定的费用;

跑费用流

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<queue>
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
const int N=100009;
const int inf=0x3f3f3f3f;
struct aa
{
	int cap,flow,pre,to,w,from;
}edge[100009];
int head[N],tot,cur[N],ww[N];
int n,m,k,lev[N];
int pre[N],dis[N],id[N],t;
bool b[N];
inline int read()
{
	int ans;char ch;
	ch='\0';while (ch<'0'||ch>'9') ch=getchar();
	ans=ch-'0';while ((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-'0';
	return ans;
}
void addedge(int x,int y,int z,int w)
{
	edge[++tot].to=y;edge[tot].pre=head[x];head[x]=tot;edge[tot].cap=z;edge[tot].w=w;
	edge[tot].from=x;
}
bool bfs()
{
	memset(lev,0,sizeof(lev));
	queue<int> q;q.push(1);lev[1]=1;
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for (int i=head[u];i;i=edge[i].pre)
		if (!lev[edge[i].to]&&edge[i].cap>edge[i].flow) 
		{
			lev[edge[i].to]=lev[u]+1;
			if (edge[i].to==n) return true;
			q.push(edge[i].to);
		}
	}
	return false;
}
int dfs(int u,int maxflow)
{
	if (u==n||maxflow==0) return maxflow;
	int ans=0,&i=cur[u];
	for (;i;i=edge[i].pre)
	if (lev[edge[i].to]==lev[u]+1)
	{
		int flow=dfs(edge[i].to,min(edge[i].cap-edge[i].flow,maxflow));
		ans+=flow;
		maxflow-=flow;
		edge[i].flow+=flow;
		edge[((i-1)^1)+1].flow-=flow;
		if (maxflow==0) return ans;
	}
	return ans;
}
int work1()
{
	int ans=0;
	while (bfs()) 
	{
		for (int i=1;i<=n;i++) cur[i]=head[i];
		ans+=dfs(1,inf);	
	}
	return ans;
}
bool spfa()
{
	memset(pre,0,sizeof(pre));
	memset(b,false,sizeof(b));
	memset(dis,inf,sizeof(dis));
	memset(id,0,sizeof(id));
	pre[1]=-1;dis[1]=0;b[1]=true;
	queue<int> q;q.push(1);
	while (!q.empty())
	{
		int u=q.front();q.pop();
		for (int i=head[u];i;i=edge[i].pre)
		if (dis[edge[i].to]>dis[u]+edge[i].w)
		if (edge[i].cap>edge[i].flow)
		{
			dis[edge[i].to]=dis[u]+edge[i].w;
			pre[edge[i].to]=u;id[edge[i].to]=i;
			if (!b[edge[i].to])
			{
				b[edge[i].to]=true;
				q.push(edge[i].to);
			}
		}
		b[u]=false;
	}
	if (dis[t]==inf) return false;
	return true;
}
int work2()
{
	int ans=0,h=tot;
	for (int i=1;i<=h;i++) 
	if (edge[i].cap>0)
	addedge(edge[i].from,edge[i].to,inf,ww[i]);//原先的正向边cap为inf
	else addedge(edge[i].from,edge[i].to,0,ww[i]);//反向边的cap应该为0,这里错了一次
	t=n+1;
	addedge(n,t,k,0);//限制扩容量
	while (spfa())
	{
		int flow=inf;
		for (int i=t;i!=1;i=pre[i])
		flow=min(flow,edge[id[i]].cap-edge[id[i]].flow);
		ans+=dis[t]*flow;
		for (int i=t;i!=1;i=pre[i])
		edge[id[i]].flow+=flow,edge[((id[i]-1)^1)+1].flow-=flow;
	}
	return ans;
}
int main()
{
	n=read();m=read();k=read();
	int x,z,y,w;
	while (m--)
	{
		x=read();y=read();z=read();w=read();
		addedge(x,y,z,0);ww[tot]=w;
		addedge(y,x,0,0);ww[tot]=-w;
	}
	printf("%d ",work1());
	printf("%d",work2());
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值