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 inpath ;
void dfs(int now , int pre , vct >&eg , vct &dp , vct &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 path ;
void getpath(int now , int pre , vct >&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 > eg(n + 1) ;
vct dp(n + 1 , 0) ;//以这个点为根,其子树中走遍所有点再回到 该点的最短路径
vct 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