BZOJ1834 [ZJOI2010]network 网络扩容【最大流+费用流】

[ Z J O I 2010 ] n e t w o r k 网 络 扩 容 [ZJOI2010]network 网络扩容 [ZJOI2010]network

Description:

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

Input Format:

  • 输入文件的第一行包含三个整数N,M,K,表示有向图的点数、边数以及所需要增加的流量。 接下来的M行每行包含四个整数u,v,C,W,表示一条从u到v,容量为C,扩容费用为W的边。

Output Format:

  • 输出文件一行包含两个整数,分别表示问题1和问题2的答案。

Sample Input:

  • 5 8 2
    1 2 5 8
    2 5 9 9
    5 1 6 2
    5 1 1 8
    1 2 8 7
    2 5 4 9
    1 2 1 1
    1 4 2 1

Sample Output:

  • 13 19
TJ:

问题分为两个部分,其中第一部分就是裸的最大流,直接写就完事儿了。
第二问其实就是一个费用流的问题了,需要重新构图,要增加K的容量,而现在免费的流量已经用完了,所以要对跑完最大流的图进行重构,重构方式如下:
①将原图中还有容量的边加到重构图中,费用为0,容量为剩余容量
②对于每一条题给的边,加入到重构图中,费用为扩容费用,容量为INF
③将当前的汇点(也就是N点),连接到一个新增节点上,建边,费用为0,容量为K

然后在重构图上跑费用流就好了,此时已经保证重构图的最大流为K了

CODE:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1111;
const int INF = 0x3f3f3f3f;
struct EDGE{
	int to,cap,rev,fee;
	bool tag;
};
int n,m,k,max_flow,min_fee,rk[MAXN],iter[MAXN],pre[MAXN],eid[MAXN],flow[MAXN],vis[MAXN],dist[MAXN];
vector<EDGE> G[MAXN],rG[MAXN];
void Input_build(){
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<=m;i++){
		int u,v,c,f;
		scanf("%d %d %d %d",&u,&v,&c,&f);
		G[u].push_back((EDGE){v,c,(int)G[v].size(),f,true});
		G[v].push_back((EDGE){u,0,(int)G[u].size()-1,f,false});
	}
}
bool bfs(){
	memset(rk,0,sizeof(rk));
	memset(iter,0,sizeof(iter));
	queue<int> que;
	rk[1] = 1;
	que.push(1);
	while(!que.empty()){
		int now = que.front();
		que.pop();
		for(int i=0;i<(int)G[now].size();i++){
			EDGE e = G[now][i];
			if(!e.cap||rk[e.to]) continue;
			rk[e.to] = rk[now]+1;
			que.push(e.to);
		}
	}
	return rk[n]!=0;
}
int dfs(int now,int f){
	if(now==n) return f;
	for(int &i=iter[now];i<(int)G[now].size();i++){
		EDGE &e = G[now][i];
		int to = e.to;
		if(!e.cap||rk[e.to]!=rk[now]+1) continue;
		int d = dfs(to,min(e.cap,f));
		if(d){
			e.cap-=d;
			G[e.to][e.rev].cap+=d;
			return d;
		}
	}
	return 0;
}
int Dinic(){
	int flow = 0;
	while(bfs()){
		int d = dfs(1,INF);
		while(d){
			flow+=d;
			d = dfs(1,INF);
		}
	}
	return flow;
}
void rebuild(){
	for(int i=1;i<=n;i++){
		for(int j=0;j<(int)G[i].size();j++){
			EDGE e = G[i][j];
			if(!e.tag) continue;
			if(e.cap){
				rG[i].push_back((EDGE){e.to,e.cap,(int)rG[e.to].size(),0,true});
				rG[e.to].push_back((EDGE){i,0,(int)rG[i].size()-1,0,true});
			}
			rG[i].push_back((EDGE){e.to,INF,(int)rG[e.to].size(),e.fee,true});
			rG[e.to].push_back((EDGE){i,0,(int)rG[i].size()-1,-e.fee,true});
		}
	}
	rG[n].push_back((EDGE){n+1,k,(int)rG[n+1].size(),0,true});
	rG[n+1].push_back((EDGE){n,0,(int)rG[n].size()-1,0,true});
}
bool Dijkstra(){
	memset(dist,INF,sizeof(dist));
	memset(vis,0,sizeof(vis));
	dist[1] = 0; flow[1] = INF;
	pre[n] = 0; pre[1] = 0;
	while(true){
		int pos = -1;
		for(int i=1;i<=n;i++) if(!vis[i]&&(pos==-1||dist[i]<dist[pos])) pos = i;
		if(pos==-1) break;
		vis[pos] = 1;
		for(int i=0;i<(int)rG[pos].size();i++){
			EDGE &e = rG[pos][i];
			if(!e.cap) continue;
			if(dist[pos]+e.fee<dist[e.to]){
				dist[e.to] = dist[pos]+e.fee;
				pre[e.to] = pos;
				eid[e.to] = i;
				flow[e.to] = min(e.cap,flow[pos]);
			}
		}
	}
	return pre[n]!=0;
}
int mincost_maxflow(){
	n++;
	int cost = 0;
	while(Dijkstra()){
		int rt = n;
		while(rt!=1){
			EDGE &e = rG[pre[rt]][eid[rt]];
			cost+=e.fee*flow[n];
			e.cap-=flow[n];
			rG[e.to][e.rev].cap+=flow[n];
			rt = pre[rt];
		}
	}
	return cost;
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("1834/6.in","r",stdin);
	#endif
	Input_build();
	max_flow = Dinic();
	rebuild();
	min_fee = mincost_maxflow();
	printf("%d %d\n",max_flow,min_fee);
	#ifndef ONLINE_JUDGE
	freopen("1834/6.out","r",stdin);
	scanf("%d %d",&max_flow,&min_fee);
	printf("RESAULT:%d %d\n",max_flow,min_fee);
	#endif
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值