[bzoj2324][ZJOI2011]营救皮卡丘 上下界费用流+floyd

2324: [ZJOI2011]营救皮卡丘

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 2502   Solved: 1037
[ Submit][ Status][ Discuss]

Description

皮卡丘被火箭队用邪恶的计谋抢走了!这三个坏家伙还给小智留下了赤果果的挑衅!为了皮卡丘,也为了正义,小智和他的朋友们义不容辞的踏上了营救皮卡丘的道路。

火箭队一共有N个据点,据点之间存在M条双向道路。据点分别从1N标号。小智一行K人从真新镇出发,营救被困在N号据点的皮卡丘。为了方便起见,我们将真新镇视为0号据点,一开始K个人都在0号点。

由于火箭队的重重布防,要想摧毁K号据点,必须按照顺序先摧毁1K-1号据点,并且,如果K-1号据点没有被摧毁,由于防御的连锁性,小智一行任何一个人进入据点K,都会被发现,并产生严重后果。因此,在K-1号据点被摧毁之前,任何人是不能够经过K号据点的。

为了简化问题,我们忽略战斗环节,小智一行任何一个人经过K号据点即认为K号据点被摧毁。被摧毁的据点依然是可以被经过的。

K个人是可以分头行动的,只要有任何一个人在K-1号据点被摧毁之后,经过K号据点,K号据点就被摧毁了。显然的,只要N号据点被摧毁,皮卡丘就得救了。

野外的道路是不安全的,因此小智一行希望在摧毁N号据点救出皮卡丘的同时,使得K个人所经过的道路的长度总和最少。

请你帮助小智设计一个最佳的营救方案吧!

Input

第一行包含三个正整数NMK。表示一共有N+1个据点,分别从0N编号,以及M条无向边。一开始小智一行共K个人均位于0号点。 

接下来M行,每行三个非负整数,第i行的整数为AiBiLi。表示存在一条从Ai号据点到Bi号据点的长度为Li的道路。

Output

仅包含一个整数S,为营救皮卡丘所需要经过的最小的道路总和。

Sample Input

3 4 2
0 1 1
1 2 1
2 3 100
0 3 1

Sample Output

3
【样例说明】
小智和小霞一起前去营救皮卡丘。在最优方案中,小智先从真新镇前往1号点,接着前往2号据点。当小智成功摧毁2号据点之后,小霞从真新镇出发直接前往3号据点,救出皮卡丘。

HINT

对于100%的数据满足N ≤ 150, M ≤ 20 000, 1 ≤ K ≤ 10, Li ≤ 10 000, 保证小智一行一定能够救出皮卡丘。至于为什么K ≤ 10,你可以认为最终在小智的号召下,小智,小霞,小刚,小建,小遥,小胜,小光,艾莉丝,天桐,还有去日本旅游的黑猫警长,一同前去大战火箭队。

Source

必须走过所有的点,那我们只需为每个点弄一个下界,跑费用流即可
inf为何赋值错了,让我调了20分钟
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define INF 1000000000
using namespace std;
const int N = 305;
int last[N], q[N*4], d[N], cnt=1, T, S, n, m, k, ans;
bool inq[N];
int dis[N][N];
struct Edge{ int to,v,next,c; }e[100005];
void insert( int u, int v, int w, int c ){
	e[++cnt].to = v; e[cnt].next = last[u]; last[u] = cnt; e[cnt].v = w; e[cnt].c = c;
	e[++cnt].to = u; e[cnt].next = last[v]; last[v] = cnt; e[cnt].v = 0; e[cnt].c = -c;
}
void floyed(){
	for( int k = 0; k <= n; k++ )
		for( int i = 0; i <= n; i++ )
			for( int j = 0; j <= n; j++ )
				if( k <= i || k <= j )
					dis[i][j] = min( dis[i][j], dis[i][k] + dis[k][j] );
}
bool spfa(){
	memset(inq,0,sizeof(inq));
	for( int i = 0; i <= T; i++ ) d[i] = INF;
	q[0] = T; inq[T] = 1; d[T] = 0;
	int tail = 1, head = 0;
	while( tail != head ){
		int now = q[head++]; if( head == T ) head = 0;
		for( int i = last[now]; i; i = e[i].next )
			if( e[i^1].v && d[e[i].to] > d[now] - e[i].c ){
				d[e[i].to] = d[now] - e[i].c;
				if( !inq[e[i].to] ){
					inq[e[i].to] = 1; q[tail++] = e[i].to; if( tail == T ) tail = 0;
				}
			}
		inq[now] = 0;
	}
	return d[S] != INF;
}
int dfs( int x, int f ){
	inq[x] = 1;
	if( x == T ) return f;
	int w, used = 0;
	for( int i = last[x]; i; i = e[i].next )
		if( e[i].v && d[e[i].to] == d[x] - e[i].c && !inq[e[i].to] ){
			w = dfs( e[i].to, min(f-used, e[i].v) );
			e[i].v -= w; e[i^1].v += w; used += w;
			ans += w*e[i].c; if( used == f ) return f;
		}
	return used;
}
void zkw(){
	while( spfa() ){
		inq[T] = 1;
		while( inq[T] ){
			memset(inq,0,sizeof(inq));
			dfs( S, INF );
		}
	}
}
int main(){
	scanf("%d%d%d", &n, &m, &k);
	S = n*2+2; T = S+1;
	for( int i = 0; i <= n; i++ )
		for( int j = 0; j <= n; j++ )
			if( i != j ) dis[i][j] = INF;
	for( int i = 1, u, v, w; i <= m; i++ ){
		scanf("%d%d%d", &u, &v, &w);
		dis[u][v] = dis[v][u] = min( dis[u][v], w );
	}
	floyed();
	insert( S, n+1, k, 0 );
	for( int i = 1; i <= n; i++ ) {
		insert( S, i+n+1, 1, 0 );
		insert( i, T, 1, 0 );
	}
	for( int i = 0; i <= n; i++ )
		for( int j = i+1; j <= n; j++ )
			if( dis[i][j] != INF ) insert( i+n+1, j, 1, dis[i][j] );
	zkw();
	printf("%d", ans);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值