您的位置:首页 > 其它

【UVa】10917 A Walk Through the Forest 最短路+DP

2014-06-30 20:06 399 查看

Problem C: A Walk Through the Forest



Jimmy experiences a lot of stress at work these days, especially sincehis accident made working difficult. To relax after a hard day, helikes to walk home. To make things even nicer, his office is on oneside of a forest, and his
house is on the other. A nice walk throughthe forest, seeing the birds and chipmunks is quite enjoyable.
The forest is beautiful, and Jimmy wants to take a different routeeveryday. He also wants to get home before dark, so he always takes apath to make progress towards his house. He considers taking apath fromA toB
to be progress if there existsa route from B to his home that is shorter thanany possible route fromA.Calculate how many different routes through the forest Jimmy might take.

Input

Input contains several test cases followed by a line containing 0.Jimmy has numbered each intersection or joining of paths starting with 1.His office is numbered 1, and his house is numbered 2. Thefirst line of each test case gives
the number of intersections N,1 < N ≤ 1000, and the number of pathsM.The followingM lines each containa pair of intersections
a b and an integerdistance1 ≤ d ≤ 1000000 indicating a path of lengthdbetween intersectiona and a different intersection
b.Jimmy may walk a path any direction he chooses.There is at most one path between any pair of intersections.

Output

For each test case, output a single integer indicating the number of different routesthrough the forest. You may assume that this number does notexceed 2147483647.

Sample Input

5 6
1 3 2
1 4 2
3 4 3
1 5 12
4 2 34
5 2 24
7 8
1 3 1
1 4 1
3 7 1
7 4 1
7 5 1
6 7 1
5 2 1
6 2 1
0

Output for Sample Input

2
4


(apologies to) Richard Krueger

类型:最短路预处理+动态规划

题目大意:给你一个n个点m条边的无向图G,起点s(编号始终为1),终点t(编号始终为2),G上的边(u,v,w)表示存在一条无向边(u,v)且边的费用为w。现在,问从起点到终点的最短路径一共有多少条?(1 <= n <= 1000)编号从1~n。

题目分析:

率先吐嘈,题目没说m的范围,因为这个RE了三次


首先,求出每个点到终点的最短路长度d[ i ],题意中说“存在一条从B出发的路径,比所有从A出发回家的路径都短”,其实就是指d[ B ] < d[ A ],那么我们构建一副新的图G',然后将所有满足条件且存在的边(u,v)添加到G'中,易知G'就是DAG,所以可以用动态规划计数求解。

注意DAG中所有从起点到点 i 的最短路径数d[ i ]先初始化为0,然后d[ 1 ] = 1,对于所有的有向边u->v,满足d[ v ] += d[ u ]。最后d[ 2 ]就是结果。

代码一:DAG上的动态规划

代码如下:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;

#define clear( A , X ) memset ( A , X , sizeof A )
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FF( i , a , b ) for ( int i = a ; i <= b ; ++ i )

const int maxN = 1005 ;
const int maxQ = 1000005 ;
const int maxH = 1000005 ;
const int maxE = 1000005 ;
const int oo = 0x3f3f3f3f ;

struct Heap {
	int num , d ;
	Heap () {}
	Heap ( int D , int Num ) : d(D) , num(Num) {}
} ;

struct Edge {
	int v , n , w ;
	Edge () {}
	Edge ( int V , int W , int N ) : v(V) , w(W) , n(N) {}
} ;

struct Priority_Queue {
	Heap heap[maxH] ;
	int top ;
	
	void swap ( Heap &a , Heap &b ) {
		Heap tmp = a ; a = b ; b = tmp ;
	}
	
	int cmp ( const Heap a , const Heap b ) {
		return a.d < b.d ;
	}
	
	void Clear () {
		top = 0 ;
	}
	
	int Empty () {
		return 0 == top ;
	}
	
	void Push ( int d , int num ) {
		heap[top] = Heap ( d , num ) ;
		int o = top ++ , p = ( o - 1 ) >> 1 ;
		while ( o && cmp ( heap[o] , heap[p] ) ) {
			swap ( heap[o] , heap[p] ) ;
			o = p , p = ( o - 1 ) >> 1 ;
		}
	}
	
	int Front () {
		return heap[0].num ;
	}
	
	void Pop () {
		heap[0] = heap[-- top] ;
		int o = 0 , p = o , l = o * 2 + 1 , r = o * 2 + 2 ;
		while ( 1 ) {
			if ( l < top && cmp ( heap[l] , heap[p] ) ) p = l ;
			if ( r < top && cmp ( heap[r] , heap[p] ) ) p = r ;
			if ( p == o ) break ;
			swap ( heap[o] , heap[p] ) ;
			o = p , l = o * 2 + 1 , r = o * 2 + 2 ;
		}
	}
} ;

struct Dij {
	Priority_Queue Q ;
	Edge edge[maxE] ;
	int adj[maxN] , cntE ;
	int done[maxN] ;
	int d[maxN] ;
	//int p[maxN] ;
	
	void Addedge ( int u , int v , int w ) {
		edge[cntE] = Edge ( v , w , adj[u] ) ;
		adj[u] = cntE ++ ;
		edge[cntE] = Edge ( u , w , adj[v] ) ;
		adj[v] = cntE ++ ;
	}
	
	void Init () {
		cntE = 0 ;
		Q.Clear () ;
		clear ( d , oo ) ;
		//clear ( p , -1 ) ;
		clear ( adj , -1 ) ;
	}
	
	void Dijkstra ( int s ) {
		clear ( done , 0 ) ;
		d[s] = 0 ;
		Q.Push ( d[s] , s ) ;
		while ( !Q.Empty () ) {
			int u = Q.Front () ;
			Q.Pop () ;
			if ( done[u] ) continue ;
			done[u] = 1 ;
			for ( int i = adj[u] ; ~i ; i = edge[i].n ) {
				int v = edge[i].v ;
				if ( d[v] > d[u] + edge[i].w ) {
					//p[v] = u ;
					d[v] = d[u] + edge[i].w ;
					Q.Push ( d[v] , v ) ;
				}
			}
		}
	}
} ;

struct DAG_DP {
	Edge edge[maxE] ;
	int adj[maxN] , cntE ;
	int Q[maxQ] , head , tail ;
	int in[maxN] ;
	int d[maxN] ;
	
	void Addedge ( int u , int v , int w = 0 ) {
		edge[cntE] = Edge ( v , w , adj[u] ) ;
		adj[u] = cntE ++ ;
	}
	
	void Init () {
		cntE = 0 ;
		head = tail = 0 ;
		clear ( d , 0 ) ;
		clear ( in , 0 ) ;
		clear ( adj , -1 ) ;
	}
	
	void DP ( int n ) {
		FF ( i , 1 , n )
			if ( !in[i] )
				Q[tail ++] = i ;
		d[1] = 1 ;
		while ( head != tail ) {
			int u = Q[head ++] ;
			for ( int i = adj[u] ; ~i ; i = edge[i].n ) {
				int v = edge[i].v ;
				d[v] += d[u] ;
				if ( 0 == ( -- in[v] ) )
					Q[tail ++] = v ;
			}
		}
		printf ( "%d\n" , d[2] ) ;
	}
} ;

Dij D ;
DAG_DP DAG ;

void work () {
	int n , m ;
	int u , v , w ;
	while ( ~scanf ( "%d%d" , &n , &m ) && n ) {
		D.Init () ;
		DAG.Init () ;
		REP ( i , m ) {
			scanf ( "%d%d%d" , &u , &v , &w ) ;
			D.Addedge ( u , v , w ) ;
		}
		D.Dijkstra ( 2 ) ;
//		FF ( u , 1 , n )
//			printf ( "d[%d] = %d\n" , u , D.d[u] ) ;
		FF ( u , 1 , n )
			for ( int i = D.adj[u] ; ~i ; i = D.edge[i].n ) {
				int v = D.edge[i].v ;
				if ( D.d[u] > D.d[v] ) {
//					printf ( "u:%d -> v:%d\n" , u , v ) ;
					DAG.Addedge ( u , v ) ;
					++ DAG.in[v] ;
				}
			}
		DAG.DP ( n ) ;
	}
}

int main () {
	work () ;
	return 0 ;
}


方法二:记忆化搜索

代码如下:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;

#define clear( A , X ) memset ( A , X , sizeof A )
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FF( i , a , b ) for ( int i = a ; i <= b ; ++ i )

const int maxN = 1005 ;
const int maxQ = 1000005 ;
const int maxH = 1000005 ;
const int maxE = 1000005 ;
const int oo = 0x3f3f3f3f ;

struct Heap {
	int num , d ;
	Heap () {}
	Heap ( int D , int Num ) : d(D) , num(Num) {}
} ;

struct Edge {
	int v , n , w ;
	Edge () {}
	Edge ( int V , int W , int N ) : v(V) , w(W) , n(N) {}
} ;

struct Prority_Queue {
	Heap heap[maxH] ;
	int top ;
	
	void swap ( Heap &a , Heap &b ) {
		Heap tmp = a ; a = b ; b = tmp ;
	}
	
	int cmp ( const Heap a , const Heap b ) {
		return a.d < b.d ;
	}
	
	void Clear () {
		top = 0 ;
	}
	
	int Empty () {
		return 0 == top ;
	}
	
	void Push ( int d , int num ) {
		heap[top] = Heap ( d , num ) ;
		int o = top ++ , p = ( o - 1 ) >> 1 ;
		while ( o && cmp ( heap[o] , heap[p] ) ) {
			swap ( heap[o] , heap[p] ) ;
			o = p , p = ( o - 1 ) >> 1 ;
		}
	}
	
	int Front () {
		return heap[0].num ;
	}
	
	void Pop () {
		heap[0] = heap[-- top] ;
		int o = 0 , p = o , l = o * 2 + 1 , r = o * 2 + 2 ;
		while ( 1 ) {
			if ( l < top && cmp ( heap[l] , heap[p] ) ) p = l ;
			if ( r < top && cmp ( heap[r] , heap[p] ) ) p = r ;
			if ( p == o ) break ;
			swap ( heap[o] , heap[p] ) ;
			o = p , l = o * 2 + 1 , r = o * 2 + 2 ;
		}
	}
} ;

struct Dij {
	Prority_Queue Q ;
	Edge edge[maxE] ;
	int adj[maxN] , cntE ;
	int done[maxN] ;
	int d[maxN] ;
	int dp[maxN] ;
	int vis[maxN] ;
	//int p[maxN] ;
	
	void Addedge ( int u , int v , int w ) {
		edge[cntE] = Edge ( v , w , adj[u] ) ;
		adj[u] = cntE ++ ;
		edge[cntE] = Edge ( u , w , adj[v] ) ;
		adj[v] = cntE ++ ;
	}
	
	void Init () {
		cntE = 0 ;
		Q.Clear () ;
		clear ( d , oo ) ;
		clear ( dp , 0 ) ;
		clear ( vis , 0 ) ;
		//clear ( p , -1 ) ;
		clear ( adj , -1 ) ;
	}
	
	void Dijkstra ( int s ) {
		clear ( done , 0 ) ;
		d[s] = 0 ;
		Q.Push ( d[s] , s ) ;
		while ( !Q.Empty () ) {
			int u = Q.Front () ;
			Q.Pop () ;
			if ( done[u] ) continue ;
			done[u] = 1 ;
			for ( int i = adj[u] ; ~i ; i = edge[i].n ) {
				int v = edge[i].v ;
				if ( d[v] > d[u] + edge[i].w ) {
					//p[v] = u ;
					d[v] = d[u] + edge[i].w ;
					Q.Push ( d[v] , v ) ;
				}
			}
		}
	}
	
	int DP ( int u ) {
		if ( u == 1 ) return 1 ;
		if ( vis[u] ) return dp[u] ;
		vis[u] = 1 ;
		for ( int i = adj[u] ; ~i ; i = edge[i].n )
			if ( d[u] < d[edge[i].v] )
				dp[u] += DP ( edge[i].v ) ;
		return dp[u] ;
	}
} ;

Dij D ;

void work () {
	int n , m ;
	int u , v , w ;
	while ( ~scanf ( "%d%d" , &n , &m ) && n ) {
		D.Init () ;
		REP ( i , m ) {
			scanf ( "%d%d%d" , &u , &v , &w ) ;
			D.Addedge ( u , v , w ) ;
		}
		D.Dijkstra ( 2 ) ;
		printf ( "%d\n" , D.DP ( 2 ) ) ;
	}
}

int main () {
	work () ;
	return 0 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: