码迷,mamicode.com
首页 > 其他好文 > 详细

解题报告 之 HDU5294 Tricks Device

时间:2015-08-16 13:49:52      阅读:117      评论:0      收藏:0      [点我收藏+]

标签:最大流   acm   dinic   筛选最短路的边   hdu5294   

解题报告 之 HDU5294 Tricks Device


Description

Innocent Wu follows Dumb Zhang into a ancient tomb. Innocent Wu’s at the entrance of the tomb while Dumb Zhang’s at the end of it. The tomb is made up of many chambers, the total number is N. And there are M channels connecting the chambers. Innocent Wu wants to catch up Dumb Zhang to find out the answers of some questions, however, it’s Dumb Zhang’s intention to keep Innocent Wu in the dark, to do which he has to stop Innocent Wu from getting him. Only via the original shortest ways from the entrance to the end of the tomb costs the minimum time, and that’s the only chance Innocent Wu can catch Dumb Zhang. 
Unfortunately, Dumb Zhang masters the art of becoming invisible(奇门遁甲) and tricks devices of this tomb, he can cut off the connections between chambers by using them. Dumb Zhang wanders how many channels at least he has to cut to stop Innocent Wu. And Innocent Wu wants to know after how many channels at most Dumb Zhang cut off Innocent Wu still has the chance to catch Dumb Zhang. 
 

Input

There are multiple test cases. Please process till EOF. 
For each case,the first line must includes two integers, N(<=2000), M(<=60000). N is the total number of the chambers, M is the total number of the channels. 
In the following M lines, every line must includes three numbers, and use ai、bi、li as channel i connecting chamber ai and bi(1<=ai,bi<=n), it costs li(0<li<=100) minute to pass channel i. 
The entrance of the tomb is at the chamber one, the end of tomb is at the chamber N. 
 

Output

Output two numbers to stand for the answers of Dumb Zhang and Innocent Wu’s questions.
 

Sample Input

8 9 1 2 2 2 3 2 2 4 1 3 5 3 4 5 4 5 8 1 1 6 2 6 7 5 7 8 1
 

Sample Output

2 6
 

题目大意:给一个有n个节点无向图,起点为1,终点为n。对于所有1到n的最短路集合所构成的子图中,第一问是至少要删几条边才能使得残余图1无法到n,第二问是在这个子图中最多删除几条边都还可以使得1可以到达n。

分析:首先必然面临一个问题,就是如何判断一条边是不是最短路上的边。一开始的想法是跑一次最短路,然后枚举每条边(u,v),如果1到u的最短路+uv长度=1到v的最短路,那么就说明是(u,v)是最短路。很遗憾这种思路是错误的,这只能证明这是u到v的最短路,但并不一定是1到n的最短路(看图你就明白了)。

技术分享


比如上面这个图,如果按照之前的那种方法来弄,那么上面的四条边都会被加进去,下面两条边也会被加进去。但是其实是错的,因为上面的那条路根本不是最短路。所以我们需要改变方法,能够保证一条路是1 -> n的最短路的一段的方法是分别以1和N为源跑一次最短路,对于一条边(u,v),如果(1->u)min + uv +(v->N)min == (1->N)min 则是最短路(当然也等于(N->1)min )。

好了,第一个问题解决了。我们先跑两个最短路,然后把是最短路上的边筛选出来,构建成新的子图。然后求第一问,要删除几条边,那么一看就是最小割了,最小割也是最大流。那么将子图的每一条边流量设置为1,再跑一下最大流则是答案。

那么第二问怎么弄呢?思路是,求出边数最少的一条最短路,再用总边数m-这条路边数就是答案。那么我们可以在子图上跑最短路,因为之前子图的流量(也可看成权值)已经被设为1了。那么此时在以1为源头在子图上跑一下最短路就是从1->N的所有最短路中边数最少的。(这里注意了,所谓最短路中边数最少的,是我们之前已经用所有是最短路的边来构造这个子图,已经保证了权重最小,然后再看边数最少)

上代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<deque>
#include<vector>
#include<utility>
using namespace std;

const int MAXN = 2510;
const int MAXM = 300010;
const int INF = 0x3f3f3f3f;

struct Edge
{
	int from, to, cap, next;
};

Edge edge[MAXM];
Edge edge2[MAXM];
int head[MAXN];
int head2[MAXN];
int level[MAXN];
int dis1[MAXN], dis2[MAXN];
int src, des, cnt, cnt2;

void SPFA(int *dis, int src,int n)
{
	int inqueue[MAXN] = {0};
	deque<int> q;
	dis[src] = 0;
	inqueue[src] = 1;
	q.push_front( src );

	while(!q.empty())
	{
		int u = q.front();
		q.pop_front();
		for(int i = head2[u]; i !=-1;i=edge2[i].next )
		{
			int v = edge2[i].to;
			if(dis[v]>dis[u]+edge2[i].cap)
			{
				dis[v] = dis[u] + edge2[i].cap;

				if(!inqueue[v])
				{
					if(!q.empty() && dis[v] > dis[q.front()])
						q.push_front( v );
					else
						q.push_back( v );
				}
			}
		}
	}
}

void addedge( int from, int to, int cap )
{
	edge[cnt].from = from;
	edge[cnt].to = to;
	edge[cnt].cap = cap;
	edge[cnt].next = head[from];
	head[from] = cnt++;

	swap( from, to );

	edge[cnt].from = from;
	edge[cnt].to = to;
	edge[cnt].cap =  0;
	edge[cnt].next = head[from];
	head[from] = cnt++;
}

void addedge2( int from, int to, int cap )
{
	edge2[cnt2].from = from;
	edge2[cnt2].to = to;
	edge2[cnt2].cap = cap;
	edge2[cnt2].next = head2[from];
	head2[from] = cnt2++;
}

bool bfs()
{
	memset( level, -1, sizeof level );
	queue<int> q;
	while(!q.empty())
		q.pop();

	level[src] = 0;
	q.push( src );

	while(!q.empty())
	{
		int u = q.front();
		q.pop();

		for(int i = head[u]; i != -1; i = edge[i].next)
		{
			int v = edge[i].to;
			if(edge[i].cap > 0 && level[v] == -1)
			{
				level[v] = level[u] + 1;
				q.push( v );
				if(v == des) return level[des] != -1;
			}
		}
	}
	return level[des] != -1;
}

int dfs( int u, int f )
{
	if(u == des) return f;
	int tem;
	for(int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if(edge[i].cap > 0 && level[v] == level[u] + 1)
		{
			tem = dfs( v, min( f, edge[i].cap ) );
			if(tem > 0)
			{
				edge[i].cap -= tem;
				edge[i ^ 1].cap += tem;
				return tem;
			}
		}
	}
	level[u] = -1;
	return 0;
}

int Dinic()
{
	int ans = 0, tem = 0;
	while(bfs())
	{
		tem = dfs( src, INF );
		if(tem > 0)
			ans += tem;
	}
	return ans;
}


int main()
{
	int n, m;
	while(cin >> n >> m)
	{
		cnt = cnt2 = 0;
		src = 1; des = n;
		memset( head, -1, sizeof head );
		memset( head2, -1, sizeof head2 );
		memset( dis1, INF, sizeof dis1 );
		memset( dis2, INF, sizeof dis2 );

		int a, b, c;
		for(int i = 1; i <= m; i++)
		{
			scanf( "%d%d%d",&a,&b,&c);
			addedge2( a, b, c );
			addedge2( b, a, c );
		}

		SPFA( dis1, 1, n );
		SPFA( dis2, n, n );

		for(int i = 1; i <= n; i++) //重构图
		{
			for(int j = head2[i]; j != -1; j = edge2[j].next)
			{
				int v = edge2[j].to;
				if(dis1[i] + edge2[j].cap + dis2[v] == dis1[n])
				{
					addedge( i, v, 1 );
					edge2[j].cap = 1;
				}
				else
				{
					edge2[j].cap = INF;
				}
			}
		}

		memset( dis1, INF, sizeof dis1 );
		SPFA(dis1, 1, n );
		cout << Dinic()<<" " << m-dis1[n]<<endl;
	}
	return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

解题报告 之 HDU5294 Tricks Device

标签:最大流   acm   dinic   筛选最短路的边   hdu5294   

原文地址:http://blog.csdn.net/maxichu/article/details/47699869

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!