【ZJOI2010】网络扩容

版权声明:小蒟蒻的博客转载也请注明出处哦 https://blog.csdn.net/qq_42835823/article/details/86607928

【ZJOI2010】网络扩容

最大流+费用流

洛谷地址

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

第一问就直接跑最大流。

第二问目前有两种方法:

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

#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;
}

猜你喜欢

转载自blog.csdn.net/qq_42835823/article/details/86607928
今日推荐