F. Vlad and Unfinished Business_树上dp

F. Vlad and Unfinished Business

题目大意:

在一棵树上,给出起点终点,和若干个标记点。现在要从起点开始,经过所有标记点后回到终点。问最短路程。

思路和代码:

一开始看到题目没什么想法就搁置了,仔细想想发现能做。下面给出我的想法。

先找出起点到终点的路径,因为这是一棵树所以路径是唯一的。

然后我们只要设置dp数组以i为根,走完该子树所有标记点并回来的最短。那么我们只要在回溯时进行递推即可。转移方程比较简单:

dp[now] = dp[now] + dp[nxt] + 2

最后的ans就是se路径长度加上dpi。

int n , m , k ; 
int s , e ;

map<int , bool> inpath ;

void dfs(int now , int pre , vct<vct<int> >&eg , vct<int> &dp , vct<bool> &mk){
	
	for(auto nxt : eg[now]){
		if(nxt == pre || inpath[nxt]) continue ;
		dfs(nxt , now , eg , dp , mk) ;
		if(dp[nxt] || mk[nxt]) dp[now] += dp[nxt] + 2 ;
		
	}
	
}

int ans ;
bool finde ;
vct<int> path ;

void getpath(int now , int pre , vct<vct<int> >&eg){
	if(now == e) finde = 1 ;
	
	for(int nxt : eg[now]){
		if(nxt == pre || finde) continue ;
		getpath(nxt , now , eg ) ;
	}
	if(finde){
		path.pb(now) ;
		inpath[now] = 1 ;
	}
}

void solve(){
	
	cin >> n >> k >> s >> e ;
	
	vct<vct<int> > eg(n + 1) ;
	vct<int> dp(n + 1 , 0) ;//以这个点为根,其子树中走遍所有点再回到 该点的最短路径 
	vct<bool> mk(n + 1 , 0) ;
	
	rep(i , 1 , k){
		int u ; cin >> u ;
		mk[u] = 1 ;
	}

	rep(i , 1 , n - 1){
		int u , v ; cin >> u >> v ;
		eg[u].pb(v) ;
		eg[v].pb(u) ;
	}
	ans = 0 ;
	finde = 0 ;
	path.clear() ;
	inpath.clear() ;
	
	getpath(s , -1 , eg) ;
	
	ans += path.size() - 1 ;
	for(auto x : path){
		dfs(x , -1 , eg , dp , mk) ;
		ans += dp[x] ;
	}
	
	cout << ans << "\n" ;
	 
}//code_by_tyrii 

小结:

posted @ 2022-05-07 15:58  tyrii  阅读(58)  评论(0编辑  收藏  举报