Description.
树上警察抓小偷。一名警察速度为 \(1\),多名小偷速度为 \(+\infty\),问多长时间抓到。
树点数 \(\le 50\)
Solution.
首先不可能抓不到。
其次步数不可能超过 \(2500\)(每抓完一个小偷走一遍全图)。
这启发我们可以直接暴搜每一步,并记忆化。
如果状态设为当前在 \(x\),那么 \(x\) 的各个字数内的小偷数量是影响答案的,无法记录状态。
注意到警察不会在非叶子节点走回头路,因为他没抓完一个子树的小偷怎么会回头。
所以可以记录警察移动过程,并把这个当做状态。
相当于记录了一个边,然后这条边把树切开,只需记录两边各多少小偷即可。
同时注意这是一个博弈问题,所以 dp 过程中既有 \(\min\) 又有 \(\max\)。
Coding.
点击查看代码
//是啊……你就是那只鬼了……所以被你碰到以后,就轮到我去变成鬼了……{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
char c=getchar(),f=0;x=0;
for(;c<48||c>57;c=getchar()) if(c==45) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<3)+(x<<1)+(c^48);
f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
struct edge{int to,w,nxt;}e[105];int n,et,head[55],dg[55];
inline void adde(int x,int y,int w) {e[++et]=(edge){y,w,head[x]},head[x]=et,dg[y]++;}
int S,m,cn[55],dp[105][55][55];
inline void count(int x,int fa)
{
for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa)
count(e[i].to,x),cn[x]+=cn[e[i].to];
}
inline int dfs(int E,int a,int b)
{
int &T=dp[E][a][b],x=e[E].to,fa=e[E^1].to;if(T) return T;
//printf("%d -> %d , %d %d : ??\n",e[E^1].to,e[E].to,a,b);
if(dg[x]==1) return b?T=dfs(E^1,b,0)+e[E].w:0;
int g[55];memset(g,0,sizeof(g)),g[0]=1e9;
for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa)
for(int j=a;j;j--) for(int k=j;k;k--)
g[j]=max(g[j],min(g[j-k],dfs(i,k,a+b-k)+e[i].w));
//printf("%d -> %d , %d %d : %d\n",e[E^1].to,e[E].to,a,b,g[a]);
return T=g[a];
}
int main()
{
read(n),et=1;for(int i=1,x,y,w;i<n;i++) read(x,y,w),adde(x,y,w),adde(y,x,w);
read(S,m);for(int i=1,x;i<=m;i++) read(x),cn[x]++;
count(S,S);int rs=1e9;for(int i=head[S];i;i=e[i].nxt)
rs=min(rs,dfs(i,cn[e[i].to],m-cn[e[i].to])+e[i].w);
return printf("%d\n",rs),0;
}