Loading

20210524考试—景区路线规划题解

考场上爆搜的每个点到达的概率,\(TLE\)理所当然,由于搜概率不太好记忆化,所以这个方法可能也只能到这了

code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define printf Ruusupuu=printf
#define R register int
#define int long long

using namespace std ;
const int N = 3e2 + 10 ;
typedef long long L ;
typedef double D ;

inline int read(){
	int w = 0 ; bool fg = 0 ; char ch = getchar() ;
	while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
	while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch - '0' ) , ch = getchar() ;
	return fg ? -w : w ;
}

int Ruusupuu , n , m , k , cnt , head [N] , xs , ys , ws ;
struct P{ int c , h1 , h2 ; D p ; } b [N] ;
struct E{ int fr , to , next , w ; } a [N << 6] ;
D ans1 , ans2 ;

inline void add( int f , int t , int w ){
	a [++ cnt].fr = f ;
	a [cnt].to = t ;
	a [cnt].w = w ; 
	a [cnt].next = head [f] ;
	head [f] = cnt ;
}

inline void dfs( int x , int lef , D p ){
//	printf( "THIS DFS%ld %ld\n" , x , lef ) ;
	b [x].p += p ; 
	int gnt = 0 ;
	for( R i = head [x] ; ~i ; i = a [i].next ){
		int y = a [i].to ;	
//		printf( "%ld %ld %ld %ld\n" , x , y , lef , lef - a [i].w - b [y].c ) ;
		if( lef - a [i].w - b [y].c >= 0 ) gnt ++ ;
	}
	for( R i = head [x] ; ~i ; i = a [i].next ){
		int y = a [i].to ;
		if( lef - a [i].w - b [y].c >= 0 )
		 dfs( y , lef - a [i].w - b [y].c , p / (D) gnt ) ;  
	}
}

void sc(){
	n = read() , m = read() , k = read() ; memset( head , -1 , sizeof( head ) ) ;
	for( R i = 1 ; i <= n ; i ++ ) b [i].c = read() , b [i].h1 = read() , b [i].h2 = read() ;
	for( R i = 1 ; i <= m ; i ++ ) xs = read() , ys = read() , ws = read() , add( xs , ys , ws ) , add( ys , xs , ws ) ;
} 

void work(){
	for( R i = 1 ; i <= n ; i ++ ) dfs( i , k - b [i].c , 1.0 / (D) n ) ; 
	for( R i = 1 ; i <= n ; i ++ ) ans1 += (D) b [i].h1 * b [i].p , ans2 += (D) b [i].h2 * b [i].p ;
	printf( "%.5lf %.5lf\n" , ans1 , ans2 ) ;
}

signed main(){
	sc() ;
	work() ;
	return 0 ; 
}

这个题正解方法很多,一种一种写
1.求期望

思路:直接设数组\(f[i][j]\)为在\(j\)时间处于\(i\)点时候,从此刻到游戏结束开心程度的期望,那么答案只需要把\(f[i][0]\)都加起来在除以\(n\)

对这个答案的理解就是把从每个点开始游戏的开心期望加起来再除以\(n\),由于从每个点开始游玩的概率是相等的,所以要除以一个\(n\)

式子很好推\(f[i][j]= \sum f[g][j+w[i,j]+t[g]](if(j+w[i,j]+t[g]<=K))\)
然后再把\(f [i][j]\)除以能到达的点数

\(Add\):记忆化搜索和\(Dp\)是等效的,尤其在图论里,不用考虑搜索顺序,记忆化搜索显得很方便。
但是本题很显然时间需要倒序枚举,所以两种方式都比较简单,但是有的题只能搜索(eg:聪聪与可可)

记搜code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define printf Ruusupuu=printf
#define R register int
#define int long long

using namespace std ;
const int N = 8e2 + 10 ;
typedef long long L ;
typedef double D ;

inline int read(){
	int w = 0 ; bool fg = 0 ; char ch = getchar() ;
	while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
	while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch - '0' ) , ch = getchar() ;
	return fg ? -w : w ;
}

int Ruusupuu , n , m , k , cnt , head [N] , xs , ys , ws ;
struct P{ int c , h1 , h2 ; D p ; } b [N] ;
struct E{ int fr , to , next , w ; } a [N << 6] ;
D ans1 , ans2 , f [N][N] , g [N][N] ;

inline void add( int f , int t , int w ){
	a [++ cnt].fr = f ;
	a [cnt].to = t ;
	a [cnt].w = w ; 
	a [cnt].next = head [f] ;
	head [f] = cnt ;
}

inline D dfs1( int x , int t ){
	t += b [x].c ; // printf( "%lld %lld\n" , x , t ) ; 
	if( f [x][t] ) return f [x][t] ;
	int gnt = 0 ; D ans = 0 ;
	for( R i = head [x] ; ~i ; i = a [i].next ){
		int y = a [i].to ;	
		if( t + a [i].w + b [y].c <= k ) gnt ++ ;
	}
	for( R i = head [x] ; ~i ; i = a [i].next ){
		int y = a [i].to ; 
		if( t + a [i].w + b [y].c <= k )
		ans += dfs1( y , t + a [i].w ) ;
	}
	if( gnt )  f [x][t] = b [x].h1 + ans / (D) gnt ;
	else f [x][t] = b [x].h1 ;
	return f [x][t] ;
}

inline D dfs2( int x , int t ){
	t += b [x].c ; 
	if( g [x][t] ) return g [x][t] ;
	int gnt = 0 ; D ans = 0 ;
	for( R i = head [x] ; ~i ; i = a [i].next ){
		int y = a [i].to ;	
		if( t + a [i].w + b [y].c <= k ) gnt ++ ;
	}
	for( R i = head [x] ; ~i ; i = a [i].next ){
		int y = a [i].to ; 
		if( t + a [i].w + b [y].c <= k  )
		ans += dfs2( y , t + a [i].w ) ;
	}
	if( gnt ) 
	g [x][t] = b [x].h2 + ans / (D) gnt ;
	else g [x][t] = b [x].h2 ;
	return g [x][t] ;
}

void sc(){
	n = read() , m = read() , k = read() ; memset( head , -1 , sizeof( head ) ) ;
	for( R i = 1 ; i <= n ; i ++ ) b [i].c = read() , b [i].h1 = read() , b [i].h2 = read() ;
	for( R i = 1 ; i <= m ; i ++ ) xs = read() , ys = read() , ws = read() , add( xs , ys , ws ) , add( ys , xs , ws ) ;
} 

void work(){
	for( R i = 1 ; i <= n ; i ++ ) ans1 += dfs1( i , 0 ) , ans2 += dfs2( i , 0 ) ;
	printf( "%.5lf %.5lf\n" , ans1 / (D) n , ans2 / (D) n ) ;
}

signed main(){
	sc() ;
	work() ;
	return 0 ; 
}

2.求概率,计算到达每个点的概率,最后再乘上快乐程度就是答案
挺好理解的,式子跟求期望几乎一样,就是这个东西不能记忆化搜索,因为是一个刷表的策略。
一般,填表比较好记搜,刷表转移不太好记搜?(反正我曾尝试失败了)
分析一下这样\(dp\)的好处,他的优点就在与只用刷一遍,对总状态数转移完就结束了,而原来打的暴力搜概率,对于同一种状态可能计算了很多次,导致浪费了很多时间。
并不每一次都搜到底,到该处理的时候再处理,其实考试的时候并没有意识到我打的是个暴力,也没有想到对一种状态计算了多次,导致我即使看到"善良的估分"也无动于衷

\(f [i][j]\)表示在\(j\)时间到达\(i\)点的概率,初始化就是\(f[i][t[i]]=1/n\),答案就是\(f[i][k]\)(在每个时间到达每个点的概率乘上带来的快乐程度就行了)

\(f[i][j]+=f[g][j+w[i,g]+t[g]]/gnt\)

code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define printf Ruusupuu=printf
#define R register int
#define int long long

using namespace std ;
const int N = 8e2 + 10 ;
typedef long long L ;
typedef long double D ;

inline int read(){
	int w = 0 ; bool fg = 0 ; char ch = getchar() ;
	while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
	while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch - '0' ) , ch = getchar() ;
	return fg ? -w : w ;
}

int Ruusupuu , n , m , K , cnt , head [N] , xs , ys , ws ;
struct P{ int c , h1 , h2 ; D p ; } b [N] ;
struct E{ int fr , to , next , w ; } a [N << 6] ;
D ans1 , ans2 , f [N][N] , g [N][N] ;

inline void add( int f , int t , int w ){
	a [++ cnt].fr = f ;
	a [cnt].to = t ;
	a [cnt].w = w ; 
	a [cnt].next = head [f] ;
	head [f] = cnt ;
}

void sc(){
	n = read() , m = read() , K = read() ; memset( head , -1 , sizeof( head ) ) ;
	for( R i = 1 ; i <= n ; i ++ ) b [i].c = read() , b [i].h1 = read() , b [i].h2 = read() , f [i][b [i].c] = 1 / (D) n ;
	for( R i = 1 ; i <= m ; i ++ ) xs = read() , ys = read() , ws = read() , add( xs , ys , ws ) , add( ys , xs , ws ) ;
} 

void work(){
	for( R k = 0 ; k <= K ; k ++ ){
		for( R i = 1 ; i <= n ; i ++ ) ans1 += b [i].h1 * f [i][k] , ans2 += b [i].h2 * f [i][k] ;
		for( R i = 1 ; i <= n ; i ++ ){
			if( !f [i][k] ) continue ;   
 			int gnt = 0 ;	
			for( R j = head [i] ; ~j ; j = a [j].next )
				if( k + a [j].w + b [a [j].to].c <= K ) gnt ++ ;
			if( !gnt ) continue ; 
			for( R j = head [i] ; ~j ; j = a [j].next ){
				int y = a [j].to ;
				if( k + a [j].w + b [y].c <= K ) f [y][k + a [j].w + b [y].c] += f [i][k] / (D) gnt ;//, printf( "%lld %lld %Lf\n" , y , k + a [j].w + b [y].c , f [y][k + a [j].w + b [y].c] ) ;
			}
		}
	} 
	
	printf( "%.5Lf %.5Lf\n" , ans1 , ans2 ) ;
}

signed main(){
	sc() ;
	work() ;
	return 0 ; 
}

posted @ 2021-05-26 12:24  Soresen  阅读(70)  评论(2)    收藏  举报