有下界的最小费用可行流2.0(bzoj 3876: [Ahoi2014]支线剧情)

什么是有下界的最小费用可行流?

平时来讲都是最小费用最大流,也就是在满流的前提条件下费用尽可能的少,而最小费用可行流一般不要求满流,但是每条边都有最小流量要求(比如经过边e(u,v)的流量不能少于4等等),而你只要满足所有边的最小流量要求就好,在这种情况下求出最小费用

无源汇点的网络:所有点都满足流量平衡

有源汇点的网络:除了源汇点外都满足流量平衡,源点只有流出,汇点只有流入


如何解决这类问题?

当然是将它转成最小费用最大流就好了!

那么怎么转?

先分类:

对于1:无源点汇点求网络可行流

①增加新源点和新汇点

②对于点u,设u所有流出边的下界和为otu,所有流入u的边的下界和为inu,令x=inu-otu,如果x<0,那么从u向汇点连接一条流量为|x|的边,如果x>0,那么从源点向u连接一条流量为x的边

之后求最小费用最大流,如果无法满流,则说明原题根本没有可行流

证明:考虑一条下界为1的边(u, v),上面的操作就同等于直接把这条边删掉,只要保证最后u点会多出1点流量,v点少1点流量就OK,可是怎么样才能保证呢?那就连一条u到汇点容量为1的边,再连一条源点到v点的边并保证这两条边满流!

对于2:有源点汇点的网络可行流

很好办,只要从汇点向源点连一条容量为无穷大且无下界的边,就成第一种情况了


附录:其实以上都是求有下界的最小费用可行流,并没有要求最大流或者最小流

如果有要求的话难度就更高一级别,就先不讲了,分享一个链接

http://blog.csdn.net/water_glass/article/details/6823741


3876: [Ahoi2014]支线剧情

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 1811   Solved: 1089
[ Submit][ Status][ Discuss]

Description

【故事背景】
宅男JYY非常喜欢玩RPG游戏,比如仙剑,轩辕剑等等。不过JYY喜欢的并不是战斗场景,而是类似电视剧一般的充满恩怨情仇的剧情。这些游戏往往
都有很多的支线剧情,现在JYY想花费最少的时间看完所有的支线剧情。
【问题描述】
JYY现在所玩的RPG游戏中,一共有N个剧情点,由1到N编号,第i个剧情点可以根据JYY的不同的选择,而经过不同的支线剧情,前往Ki种不同的新的剧情点。当然如果为0,则说明i号剧情点是游戏的一个结局了。
JYY观看一个支线剧情需要一定的时间。JYY一开始处在1号剧情点,也就是游戏的开始。显然任何一个剧情点都是从1号剧情点可达的。此外,随着游戏的进行,剧情是不可逆的。所以游戏保证从任意剧情点出发,都不能再回到这个剧情点。由于JYY过度使用修改器,导致游戏的“存档”和“读档”功能损坏了,
所以JYY要想回到之前的剧情点,唯一的方法就是退出当前游戏,并开始新的游戏,也就是回到1号剧情点。JYY可以在任何时刻退出游戏并重新开始。不断开始新的游戏重复观看已经看过的剧情是很痛苦,JYY希望花费最少的时间,看完所有不同的支线剧情。

Input

输入一行包含一个正整数N。
接下来N行,第i行为i号剧情点的信息;
第一个整数为Ki,表示与i连接的有Ki个剧情点,接下来Ki个整数对,Bij和Tij,表示从剧情点i可以前往剧
情点j,并且观看这段支线剧情需要花费的时间为Tij。

Output

 输出一行包含一个整数,表示JYY看完所有支线剧情所需要的最少时间。

Sample Input

6
2 2 1 3 2
2 4 3 5 4
2 5 5 6 6
0
0
0

Sample Output

24

这题就是给你一个有向无环联通图,除此之外每个点都可以瞬间回到点1,并且1的拓扑序最高

求每条边都经过至少一次的最短总耗时

因为每条边都至少经过一次,那么相当于每条边的下界流量就为1,上届显然无穷大

费用就是这条边的长度,那么就好办了


新建源点汇点

①如果x到y有条长度为z的边,那么x到y连接一条流量inf,费用为z的边(每条边除了必走的1次外可以无限走)

②所有点向点1连接一条流量inf,费用为0的边(因为你可以随时重新开始游戏)

③所有点向汇点连接一条流量为该点出度,费用为0的边

(因为所有边都要至少经过1次,那么对于x点必须要准备好out[x]的流量才行)

④源点向所有点连接一条流量为该点入度,费用为0的边

根据题意一定可行,直接套最小费用最大流模板就好了

别忘了最后答案要加上所有本身流量要求对应的费用之和(正好是所有边的长度和)

因为上面操作相当于删边!边被删了费用自然不会被统计


204ms

#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
#define LL long long
#define inf 1044266558
typedef struct Res
{
	int next;
	int to, from;
	int flow, cost;
}Road;
Road G[90005];
int head[505], vis[505], dis[505], in[505], out[505], S, T, cnt, ans; 
void Add(int u, int v, int flow, int cost)
{
	cnt++;
	G[cnt].next = head[u];
	head[u] = cnt;
	G[cnt].from = u;
	G[cnt].to = v;
	G[cnt].flow = flow;
	G[cnt].cost = cost;
}
int SPFA()
{
	int now, i, v;
	queue<int> q;
	memset(vis, 0, sizeof(vis));
	memset(dis, 62, sizeof(dis));
	q.push(S);
	vis[S] = 1;
	dis[S] = 0;
	while(q.empty()==0)
	{
		now = q.front();
		q.pop();
		vis[now] = 0;
		for(i=head[now];i!=0;i=G[i].next)
		{
			v = G[i].to;
			if(G[i].flow && dis[v]>dis[now]+G[i].cost)
			{
				dis[v] = dis[now]+G[i].cost;
				if(vis[v]==0)
				{
					vis[v] = 1,
					q.push(v);
				}
			}
		}
	}
	if(dis[T]<15000000)
		return 1;
	return 0;
}
int Sech(int now, int low)
{
	int i, w, used;
	vis[now] = 1;
	if(now==T)
		return low;
	used = low;
	for(i=head[now];i!=0;i=G[i].next)
	{
		if(G[i].flow && dis[G[i].to]==dis[now]+G[i].cost && vis[G[i].to]==0)
		{
			w = Sech(G[i].to, min(used, G[i].flow));
			G[i].flow -= w;
			G[i^1].flow += w;
			used -= w;
			ans += w*G[i].cost;
			if(used==0)
				return low;
		}
	}
	return low-used; 
}
int main(void)
{
	int i, j, n, x, y;
	while(scanf("%d", &n)!=EOF)
	{
		S = n+2, T = n+1;
		cnt = 1, ans = 0;
		memset(in, 0, sizeof(in));
		memset(out, 0, sizeof(out));
		for(i=2;i<=n;i++)
		{
			Add(i, 1, inf, 0);
			Add(1, i, 0, 0);
		}
		for(i=1;i<=n;i++)
		{
			scanf("%d", &out[i]);
			for(j=1;j<=out[i];j++)
			{
				scanf("%d%d", &x, &y);
				Add(i, x, inf, y);
				Add(x, i, 0, -y);
				in[x]++;
				ans += y;
			}
		}
		for(i=1;i<=n;i++)
		{
			x = in[i]-out[i];
			if(x<0)
			{
				Add(i, T, -x, 0);
				Add(T, i, 0, 0);
			}
			else
			{
				Add(S, i, x, 0);
				Add(i, S, 0, 0);
			}
		}
		while(SPFA()) 
		{
			memset(vis, 0, sizeof(vis));
			while(Sech(S, 1044266558))
				memset(vis, 0, sizeof(vis));
		}
		printf("%d\n", ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值