[Luogu P4043] [BZOJ 3876] [AHOI2014 JSOI2014]支线剧情

洛谷传送门

BZOJ传送门

题目背景

宅男JYY非常喜欢玩RPG游戏,比如仙剑,轩辕剑等等。不过JYY喜欢的并不是战斗场景,而是类似电视剧一般的充满恩怨情仇的剧情。这些游戏往往都有很多的支线剧情,现在JYY想花费最少的时间看完所有的支线剧情。

题目描述

JYY现在所玩的RPG游戏中,一共有 N N N个剧情点,由 1 1 1 N N N编号,第 i i i个剧情点可以根据JYY的不同的选择,而经过不同的支线剧情,前往Ki种不同的新的剧情点。当然如果为 0 0 0,则说明 i i i号剧情点是游戏的一个结局了。

JYY观看一个支线剧情需要一定的时间。JYY一开始处在 1 1 1号剧情点,也就是游戏的开始。显然任何一个剧情点都是从1号剧情点可达的。此外,随着游戏的进行,剧情是不可逆的。所以游戏保证从任意剧情点出发,都不能再回到这个剧情点。由于JYY过度使用修改器,导致游戏的“存档”和“读档”功能损坏了,

所以JYY要想回到之前的剧情点,唯一的方法就是退出当前游戏,并开始新的游戏,也就是回到 1 1 1号剧情点。JYY可以在任何时刻退出游戏并重新开始。不断开始新的游戏重复观看已经看过的剧情是很痛苦,JYY希望花费最少的时间,看完所有不同的支线剧情。

输入输出格式

输入格式:

输入一行包含一个正整数N。

接下来 N N N行,第 i i i行为 i i i号剧情点的信息;

第一个整数为,接下来个整数对, B i j B_{ij} Bij T i j T_{ij} Tij,表示从剧情点i可以前往剧情点,并且观看这段支线剧情需要花费的时间。

输出格式:

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

输入输出样例

输入样例#1:

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

输出样例#1:

24

说明

JYY需要重新开始 3 3 3次游戏,加上一开始的一次游戏, 4 4 4次游戏的进程是

样例解释

1->2->4,1->2->5,1->3->5和1->3->6。

对于100%的数据满足 N ≤ 300 , 0 ≤ K i ≤ 50 , 1 ≤ T i j ≤ 300 , S i g m a ( K i ) ≤ 5000 N\le 300,0\le K_i\le 50,1\le T_{ij}\le 300,Sigma(K_i)\le 5000 N300,0Ki50,1Tij300,Sigma(Ki)5000

解题分析

每条边都要有至少为 1 1 1的流量, 并且要求费用最小, 就是带上下界的最小费用可行流, 那么强制每条边下界为 1 1 1, 跑最小费用最大流补流即可。

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 305
#define INF 1e8
template <class C> IN C max(C a, C b) {return a > b ? a : b;}
template <class C> IN C min(C a, C b) {return a < b ? a : b;}
template <class C> IN void in(C &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int n, cnt, S, T, sum, TT;
int head[MX], dis[MX], del[MX], pre[MX], deg[MX];
bool inq[MX];
struct Edge {int to, fl, len, nex;} edge[200500];
IN void add(R int from, R int to, R int fl, R int len)
{
	edge[++cnt] = {to, fl, len, head[from]}, head[from] = cnt;
	edge[++cnt] = {from, 0, -len, head[to]}, head[to] = cnt;
}
namespace MCMF
{
	std::queue <int> q;
	IN bool SPFA()
	{
		std::memset(dis, 63, sizeof(dis));
		dis[S] = 0; del[S] = INF; q.push(S); R int now;
		W (!q.empty())
		{
			now = q.front(); q.pop();
			for (R int i = head[now]; ~i; i = edge[i].nex)
			{
				if (dis[edge[i].to] > dis[now] + edge[i].len && edge[i].fl)
				{
					dis[edge[i].to] = dis[now] + edge[i].len;
					del[edge[i].to] = min(del[now], edge[i].fl);
					pre[edge[i].to] = i;
					if (!inq[edge[i].to]) inq[edge[i].to] = true, q.push(edge[i].to);
				}
			}
			inq[now] = false;
		}
		return dis[T] ^ dis[0];
	}
	IN void update()
	{
		R int now = T, pr;
		sum += del[T] * dis[T];
		W (now ^ S)
		{
			pr = pre[now];
			edge[pr].fl -= del[T], edge[pr ^ 1].fl += del[T];
			now = edge[pr ^ 1].to;
		}
	}
	IN void init()
	{
		W (SPFA()) update();
		printf("%d", sum);
	}
}
int main(void)
{
	std::memset(head, cnt = -1, sizeof(head));
	in(n); S = n + 1, TT = n + 2, T = n + 3;
	int foo, a, b;
	for (R int i = 1; i <= n; ++i)
	{
		in(foo);
		for (R int j = 1; j <= foo; ++j)
		{
			in(a), in(b);
			deg[i]--, deg[a]++; sum += b;
			add(i, a, INF, b);
		}
	}
	for (R int i = 2; i <= n; ++i) add(i, TT, INF, 0);
	for (R int i = 1; i <= TT; ++i)
	if (deg[i] > 0) add(S, i, deg[i], 0);
	else add(i, T, -deg[i], 0);
	add(TT, 1, INF, 0);
	MCMF::init();
}

你可能感兴趣的:(网络流,费用流)