【ZJOI2010】网络扩容

【ZJOI2010】网络扩容

最大流+费用流

洛谷地址

在这里插入图片描述
在这里插入图片描述

第一问就直接跑最大流。

第二问目前有两种方法:

  • 保留残留网络,设费用为 0 0 0,然后复制原网络,但是容量为 i n f inf inf,费用即扩容用。 然后建立一个起点,向1连一条容量为 K K K,费用为 0 0 0的边。 然后在新图上跑最小费用最大流即可。(可以保证新流量为 K K K
  • 将参与网络的正边容量加上 K K K,加入扩容费用,跑费用流。(最初的想法,但是错误的,因为通往终点的不止有一条路,所以增加的流量应是 n ∗ K n*K nK

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x7fffffff
int n,m,k,s,t;
struct edge{
	int u,v,c,w,nxt;
}e[100005];
int first[50005],cur[50005],cnt=1;
inline void add(int u,int v,int c,int w){
	e[++cnt].v=v;e[cnt].c=c;e[cnt].w=w;e[cnt].u=u;
	e[cnt].nxt=first[u];first[u]=cnt;
}
//最大流
int dep[50005];
int bfs(){
	queue<int>q;
	q.push(s);
	memset(dep,0,sizeof(dep));
	dep[s]=1;
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=first[x];i;i=e[i].nxt){
			int y=e[i].v;
			if(e[i].c<1||dep[y])continue;//
			dep[y]=dep[x]+1;
			q.push(y);
			if(y==t)return 1;
		}
	}
	return 0;
}
int dfs(int x,int f){
	if(x==t||f==0)return f;
	int used=0;
	for(int &i=cur[x];i;i=e[i].nxt){
		int y=e[i].v;
		if(e[i].c&&dep[y]==dep[x]+1){
			int r=dfs(y,min(e[i].c,f));
			if(!r)continue;
			used+=r;f-=r;
			e[i].c-=r;e[i^1].c+=r;
			if(!f)break;
		}
	}
	if(!used)dep[x]=-1;
	return used;
}
int dinic(){ 
	int flow=0;
	while(bfs()){//cout<<1;
		memcpy(cur,first,sizeof(first));
		flow+=dfs(s,INF);
	}
	return flow;
}
//费用流 
int money=0,vis[50005],dis[50005];
int spfa(){//反向 
	memset(vis,0,sizeof(vis));//是否在队列里 
	memset(dis,0x3f,sizeof(dis));//最短路 
	dis[t]=0;vis[t]=1;
	deque<int>q;//双端队列
	q.push_back(t);
	while(!q.empty()){
		int u=q.front();q.pop_front();vis[u]=0;
		for(int i=first[u];i;i=e[i].nxt){
			if(e[i^1].c&&dis[u]-e[i].w<dis[e[i].v]){//反向的反向   反向的是负边 
				dis[e[i].v]=dis[u]-e[i].w;//
				if(!vis[e[i].v]){
					vis[e[i].v]=1;//~~~ 
					if(!q.empty()&&dis[e[i].v]<dis[q.front()])
						q.push_front(e[i].v);
					else q.push_back(e[i].v);//如果最短路比队首小,就放队首让它先松弛其他点 
				}
			}
		}
	}
	//for(int i=1;i<=n;i++)cout<<dis[i]<<" ";
	//cout<<endl;
	return dis[s]<0x3f3f3f3f;//真1假0 
}
int dfs2(int x,int f){
	if(x==t){//f 费用 
		vis[t]=1;return f;
	}
	int used=0,k;vis[x]=1;
	for(int i=first[x];i;i=e[i].nxt){
		int v=e[i].v,c=e[i].c,ff=e[i].w;
		if(!vis[v]&&c&&dis[x]-ff==dis[v]){//满足最短路 
			k=dfs2(v,min(c,f-used));
			if(k){
				money+=k*ff;used+=k;
				e[i].c-=k;e[i^1].c+=k;
			}//
			if(used==f)break;
		}
	}
	return used;
} 
int ZKW(){
	int flow=0;
	while(spfa()){
		vis[t]=1;
		while(vis[t]){
			memset(vis,0,sizeof(vis));
			flow+=dfs2(s,INF);
		}
	}
	return flow;
}
int h[100005];
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++){
		int u,v,c;
		scanf("%d%d%d%d",&u,&v,&c,&h[i*2]);
		h[i*2+1]=-h[i*2];
		add(u,v,c,0);add(v,u,0,0);
	}
	s=1;t=n;
	printf("%d ",dinic());
	int g=cnt;
	for(int i=2;i<=g;i++)
		if(i%2==0)add(e[i].u,e[i].v,INF,h[i]);
		else add(e[i].u,e[i].v,0,h[i]);
	s=++n;
	add(s,1,k,0);add(1,s,0,0);
	ZKW();
	printf("%d",money);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值