【floyd】JAG Spring 2013 Revenge of Minimum Cost Flow aizu 2561

2 篇文章 0 订阅
1 篇文章 0 订阅
本文探讨了一种特殊情况下的最小费用流问题,其中费用是基于分段线性函数,并且存在一种特殊边使得单位流量费用随流量增加而增加。通过分析,即使有这种特殊情况,也能使用Floyd算法在特定复杂度下找到解决方案。文中还提到,当没有这种特殊边时,问题简化为最短路问题,并讨论了如何处理A[i] < B[i]的情况,以及不枚举分叉点和合并点可能导致的问题,并给出了验证数据。
摘要由CSDN通过智能技术生成

题意:求s到t的流量为f的最小费用流,每条边的费用是一个分段函数,当流量小于等于D[i]时,为A[i]*flow,超过时为A[i]*D[i]+B[i]*(flow-D[i]),最多只有一条边的A[i]<B[i]

一开始听叉姐提到了这个题,觉得很有趣,然后估摸着接下来也不会训这套题,于是就从官网下数据把这道题过了...

虽然说是费用流,但是其实是没有容量限制的,因此比费用流的限制要宽松很多,然后假如没有A[i],B[i]之分的话,其实就是个s到t的最短路,然后考虑有A[i]和B[i]之分,但是没有A[i]<B[i]的边,那么可以看到任意两点i->j的流量为F的流其实只会选择一条路径,假设有两条不相交路径从i到j,其中一条的流量为d,另一条流量为F-d的话,那么如果一条退1的流量,另一条加1的流量,如果费用不变,那么可以一直做到改变为止,那么由于每条路径流的越多,其单位流量费用因为A[i]>B[i]的缘故,其实是个递减的函数,因此必然存在其中一条退1的流量,另一条加1的流量会使总费用减小,然后一旦开始这个退流增流的过程,同样因为其中一个单位流量费用是减函数,另一个单位流量费用是增函数,因此会其中一个会不停退流,另一个不停地增流,直到最后两点间只有一条路径有流量,于是不考虑A[[i]<B[i]的边就可以由o(n^3)的floyd求出任意两点间流量任意的答案,因为一旦流量限定,每条边的费用就确定了。

然后考虑A[i]<B[i],他的单位流量费用是个增函数,因此前面的推理就不成立了,但是可以预见,必然至多分出一个经过A[i]<B[i]这条边的路径,假设f[p][i][j]为流量为F的从i到j的最小费用流,这个可以在o(F*n^3)的时间内算出,我们可以由f推出任意两点间经过两条路径的最小费用流,即

d[i][j]=min{f[p][i][l]+cost[l][r]+f[p][r][j]+f[F-p][i][j]}

l->r即为A[i]<B[i]的边

考虑这样转移时,两条路径可能会有重叠部分,但是不难想象,除了l->r这条边,其他边的单位流量费用是减函数,因此如果重合的话,其实是不优的,所以当我们再枚举从那两个点开始分叉和合并后,那些不合法的路径都会因为取min而被去掉。然后为了验证这个结论,我特地把枚举在那两个点分叉和合并这个过程去掉,结果发现竟然还是过了所有数据,但是我随手构个数据

3 3 0 2 3
0 1 3 2 2
1 2 3 2 1
1 2 1 1000 1

就可以把不枚举分叉点和合并点的程序给cha掉,说明日本的数据还是有点水的...

第一步预处理f的复杂度o(f*n^3),第二步求d的复杂度为o(f*n^2),最后一步求答案的复杂度为o(n^2),总复杂度o(f*n^3)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
const int oo=1073741819;
using namespace std;
struct point{
	int x,y,A,B,D;
}G[2005];
int f[205][105][105],g[105][105][3],b[105][105],d[205][105][105];
int n,m,s,t,F;
inline void Up(int &x,int y)
{
	if (x>y) x=y;
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	scanf("%d%d%d%d%d",&n,&m,&s,&t,&F);
	s++,t++;
	int id=0;
	for (int i=1;i<=m;i++) {
		scanf("%d%d%d%d%d",&G[i].x,&G[i].y,&G[i].A,&G[i].B,&G[i].D);
		G[i].x++,G[i].y++;
		if (G[i].A<G[i].B) id=i;
	}
	for (int P=1;P<=F;P++) {
		for (int i=1;i<=n;i++)
			for (int j=1;j<=n;j++) 
				if (i!=j) f[P][i][j]=d[P][i][j]=oo;else f[P][i][j]=d[P][i][j]=0;
		for (int i=1;i<=m;i++) {
			if (G[i].A<G[i].B) continue;
			int x=G[i].x,y=G[i].y;
			if (P>G[i].D) Up(f[P][x][y],G[i].A*G[i].D+G[i].B*(P-G[i].D));
			else Up(f[P][x][y],P*G[i].A);
		}
		for (int k=1;k<=n;k++)
			for (int i=1;i<=n;i++)
				if (f[P][i][k]<oo)
					for (int j=1;j<=n;j++)
						if (f[P][k][j]<oo)
							Up(f[P][i][j],f[P][i][k]+f[P][k][j]);
	}
	if (id)
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			for (int p=0;p<=F;p++) {
				int cos=0;
				if (p>G[id].D) cos=G[id].A*G[id].D+G[id].B*(p-G[id].D);
				else cos=G[id].A*p;
				if (f[F-p][i][j]<oo && f[p][i][G[id].x]<oo && f[p][G[id].y][j]<oo)
					Up(d[F][i][j],f[F-p][i][j]+f[p][i][G[id].x]+cos+f[p][G[id].y][j]);
			}
	int ans=min(f[F][s][t],d[F][s][t]);
	//cout<<f[F][s][s]<<endl;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			if (f[F][s][i]<oo && d[F][i][j]<oo && f[F][j][t]<oo)
				Up(ans,f[F][s][i]+d[F][i][j]+f[F][j][t]);
	if (ans<oo) printf("%d\n",ans);
	else printf("Impossible\n");
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值