【最大流+最小费用流】网络扩容

被虐惨了。。做了好久才做出来。


二、网络扩容(network.c/cpp/pas)

时限:1秒

【问题描述】

    给定一张有向图,每条边都有一个容量C和一个扩容费用W。这里扩容费用是指将容量扩大1所需的费用。求:

1、  在不扩容的情况下,1到N的最大流;

2、  将1到N的最大流增加K所需的最小扩容费用。

【输入格式】network.in

    输入文件的第一行包含三个整数N,M,K,表示有向图的点数、边数以及所需要增加的流量。

    接下来的M行每行包含四个整数u,v,C,W,表示一条从u到v,容量为C,扩容费用为W的边。

【输出格式】network.out

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

【输入样例】

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

【输出样例】

    13 19

【数据规模】

    30%的数据中,N<=100

    100%的数据中,N<=1000,M<=5000,K<=10


思想想清楚之后很好理解。主要要翻过那个弯。

求出最大流之后,我们要尽量使用还有残量的边或者价格小的边,因此可以朝最小费用流去想。

但是如果残量还有的边用完了,这条边可能还能够扩容。处理方法就是,把这条边变成两条。。。

一条是原来的边,他的费用是0,一条边是新的边,容量无限(最后在超级源限制总容量为K就行了,现在已经有总流量了,限制为K相当于能够增加K),价格为题目给的。


我们已经求出了最大流,但是根据最大流求出的费用流也许不是最优,但是能够退流(即相当于减小费用),所以最后求出的费用一定是最小的。


一开始考虑错了一个地方,就导致没有退流。我重建边的时候考虑的是原来有没有的边,但没有读入的边也可能需要建边,因为有的反向边在Sap过程中也有了残量,因此他们也要建边。



#include <cstdio>

#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))

const char fi[] = "network.in";
const char fo[] = "network.out";
long N;long M;long K;

void init_file()
{
 	freopen(fi,"r",stdin);
	freopen(fo,"w",stdout);
}

const long maxn = 1010;
const long MOD = 100000;
struct node
{
	long u;
	long v;
	node* nxt;	
	node* bck;
	long c;
	long w;
};
node* head[maxn];
node* head2[maxn];
long rv[maxn][maxn];
long D[maxn];
long DN[maxn];
long dist[maxn];
node* pre[maxn];
long que[MOD];
bool vis[maxn];
long c[maxn][maxn];
long w[maxn][maxn];

void insert(long u,long v,long c,long w)
{
	node* nn = new node;
	nn-> u = u;
	nn-> v = v;
	nn-> c = c;
	nn-> w = w;
	nn-> nxt = head[u];
	head[u] = nn;
}

void insert2(long u,long v,long c,long w)
{
	node* nn = new node;
	nn-> u = u;
	nn-> v = v;
	nn-> c = c;
	nn-> w = w;
	nn-> nxt = head2[u];
	head2[u] = nn;
}

void readdata()
{
	scanf("%ld%ld%ld",&N,&M,&K);
	for (long i=1;i<M+1;i++)
	{
		long u;long v;long _c;long _w;
		scanf("%ld%ld%ld%ld",&u,&v,&_c,&_w);
		insert(u,v,0,0);
		rv[u][v] += _c;
		if (w[u][v])
			w[u][v] = MIN(w[u][v],_w);
		else 
			w[u][v] = _w;  
		c[u][v] += _c;
	}
}
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
const long inf = 0x7f7f7f7f;
long Sap(long u,long maxflow)
{
	if (u == N)
		return maxflow;
	long tmp = 0;
	for (node* vv=head[u];vv;vv=vv->nxt)
	{
		long v = vv->v;
		if (rv[u][v]>0&&D[v]+1==D[u])
		{
			long fb = Sap(v,MIN(rv[u][v],maxflow-tmp));
			tmp += fb;
			rv[u][v] -= fb;	
			rv[v][u] += fb;
			if (tmp == maxflow)
				return tmp;
		}
	}
	if (D[1]>=N)
		return tmp;
	DN[D[u]]--;
	if (DN[D[u]] == 0)
		D[1] = N;
	DN[++D[u]]++;
	return tmp;
}

bool spfa()
{
	for (long i=0;i<N+1;i++)
	{
		dist[i] = inf;
		pre[i] = 0;
	}		
	dist[0] = 0;
	long l = 0;
	long r = 0;
	que[++r] = 0;
	while (l != r)
	{
		l = (l+1) % MOD;
		long u = que[l];
		vis[u] = false;
		for (node* vv=head2[u];vv;vv=vv->nxt)
		{
			long v = vv->v;
			if (vv->c>0 && dist[v]>dist[u]+vv->w)
			{
				pre[v] = vv;
				dist[v] = dist[u]+vv->w;
				if (!vis[v])
				{
					vis[v] = true;
					r = (r+1) % MOD;
					que[r] = v;
				}	
			}
		}	
	}
	return pre[N];
}

void work()
{
	DN[0] = N;
	long maxflow = 0;
	while (D[1] < N)
		maxflow += Sap(1,inf);
	printf("%ld ",maxflow);
	
	for (long u=0;u<N+1;u++)
	{
		for (long v=u+1;v<N+1;v++)
		{
			if (rv[u][v])
				insert2(u,v,rv[u][v],0);
			if (rv[v][u])
				insert2(v,u,rv[v][u],0);
			if (rv[u][v] && rv[v][u])
			{
				head2[u]->bck = head2[v];
				head2[v]->bck = head2[u];			
			}
			if (rv[u][v] && !rv[v][u])	
				head2[u]->bck = 0;
			if (rv[v][u] && !rv[u][v])
				head2[v]->bck = 0;
				
			if (c[u][v])	
				insert2(u,v,inf,w[u][v]);
			if (c[v][u])
				insert2(v,u,inf,w[v][u]);
			if (c[u][v] && c[v][u])
			{
				head2[u]->bck = head2[v];
				head2[v]->bck = head2[u];			
			}
  			if (c[u][v] && !c[v][u])	
				head2[u]->bck = 0;
			if (c[v][u] && !c[u][v])
				head2[v]->bck = 0;
		}
	}
	
	insert2(0,1,K,0);
	head2[0]->bck = 0;
	
	long ans2 = 0;
	while (1)
	{
		if (!spfa()) break;
		long min = inf;
		node* p = pre[N];
		while (p)
		{
			if (p->c < min)
				min = p->c;
			p = pre[p->u];
		}
		ans2 += min*dist[N];
		p = pre[N];
		while (p)
		{
			p->c -= min;
			if (p->bck)
				p->bck->c += min;
			p = pre[p->u];
		}
	}
	printf("%ld",ans2);
}

int main()
{
	init_file();
	readdata();
	work();
	return 0;	
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值