Bzoj:[ZJOI2010]network 网络扩容:网络流,最大流+费用流

13 篇文章 0 订阅

题目链接:1834:[ZJOI2010]network 网络扩容

好端端的一道水题让我给做的……

一开始我想的是先跑一边dinic得到残量网络,反正K不大然后就跑K遍费用流,每次跑费用流时碰到一条正向边没流满就把边权设为0,否则流量+1,边权为最开始的权值

但是这样傻X都能看出来是错的QAQ

加入跑了一遍费用流以后我们流量加过1的边并没有流到,那么他的权值就成0了,成0了,成0了……然后就GG了……

我还满心欢喜的写了半天,错了还以为是模板打错了QAQ

荏弱没话说……

这题我们一遍dinic后对于剩余图,只要这张图上的边是正边就复制一条容量为inf费用不变,反向边复制一条容量为0费用相反,并把原来的边的边权改为0

注意是复制原来的边保留,这样再次跑的时候由于原来的剩余边费用都为0会被优先选择

于是就对了……我还是太naive……

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=2000+10;
const int inf=0x7fffffff/2-1;
int n,m,h[maxn],tot=1,S,T,K,vis[maxn];
struct edge{int to,next,w,c,f;}G[maxn*10];
int dis[maxn],pre[maxn],cur[maxn];

bool bfs(){
	for (int i=S;i<=T;++i) vis[i]=-1;
	queue<int>q; q.push(S); vis[S]=0;
	while (!q.empty()){
		int u=q.front(); q.pop();
		for (int i=h[u];i;i=G[i].next){
			int v=G[i].to;
			if (vis[v]==-1&&G[i].c>0){
				vis[v]=vis[u]+1; q.push(v);
			}
		}
	}return vis[T]!=-1;
}

int dfs(int x,int f){
	if (x==T||f==0) return f;
	int w,used=0;
	for (int i=cur[x];i;i=G[i].next)
	    if (G[i].c&&vis[x]+1==vis[G[i].to]){
			w=f-used;
			w=dfs(G[i].to,min(w,G[i].c));
			G[i].c-=w; G[i^1].c+=w;
			used+=w; if (used==f) return f;
			if (G[i].c) cur[x]=i;
	    }
	if (!used) vis[x]=-1;
	return used;
}

void dinic(){
	int ret=0;
	while (bfs()){
		for (int i=S;i<=T;++i) cur[i]=h[i];
		ret+=dfs(S,inf);
	}printf("%d ",ret);
}

void add(int x,int y,int c,int w){
	G[++tot].to=y; G[tot].next=h[x]; h[x]=tot;
	G[tot].c=c; G[tot].w=w; G[tot].f=x;
}

void rebuild(){
	int sum=tot; S=0; add(S,1,K,0); add(1,S,0,0);
	for (int i=2;i<=sum;++i)
		if (!(i&1)) add(G[i].f,G[i].to,inf,G[i].w),
					add(G[i].to,G[i].f,0,-G[i].w),
					G[i].w=G[i^1].w=0;
}

bool spfa(){
	for (int i=S;i<=T;++i){vis[i]=0;dis[i]=inf;pre[i]=-1;}
	vis[S]=1; dis[S]=0; deque<int>q; q.push_front(S);
	while (!q.empty()){
		int u=q.front(); q.pop_front(); vis[u]=0;
		for (int i=h[u];i;i=G[i].next){
			int v=G[i].to;
			if (dis[v]>dis[u]+G[i].w&&G[i].c>0){
				dis[v]=dis[u]+G[i].w;
				pre[v]=i; if (!vis[v]){ vis[v]=1;
					if (!q.empty()&&dis[v]<dis[q.front()])
						q.push_front(v);
					else q.push_back(v);
				}
			}
		}
	}return pre[T]!=-1;
}

void min_cost_flow(){
	int cost=0;
	while (spfa()){
        int mf=inf; 
		for (int i=pre[T];i!=-1;i=pre[G[i^1].to])
		    mf=min(mf,G[i].c);
		for (int i=pre[T];i!=-1;i=pre[G[i^1].to])
		    {G[i].c-=mf;G[i^1].c+=mf;cost+=G[i].w*mf;}
	}printf("%d",cost);
}

int main(){
    scanf("%d%d%d",&n,&m,&K); S=1; T=n;
	for (int i=1;i<=m;++i){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		add(a,b,c,d); add(b,a,0,-d);
	}
	dinic(); rebuild(); 
	min_cost_flow();
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值