【Codeforces 868E】Policeman and a Tree

树形DP。

题目大意:

给定一棵树,带点权。有一个警察,一开始在 $s$ 点,每秒只能移动一格。有 $m$ 个小偷分布在树上,速度无穷大。警察和小偷在同一位置时,小偷会被抓住。假设警察和小偷足够聪明,求抓住所有小偷的最短时间。

题解:

首先肯定有解,因为每次只盯牢一个的话,把他逼到叶子上就抓住了。

考虑树形DP。

令 $f[u][v][x][y]$ 表示当前在 $v$,是从 $u$ 走过来的,$v$ 关于 $u$ 的子树里有 $x$ 个小偷,另外位置有 $y$ 个小偷,抓完所有小偷的方案数。

当 $v$ 是叶子的时候,$f[u][v][x][y]=f[v][u][y][0]+[y=0]\times \mathrm{dist}(u,v)$,表示逼死了这 $x$ 个,如果剩下还有那么回头抓剩下的。

对任意 $u,v$,有 $f[u][v][0][0]=0$。

对于剩下的情况,小偷会分布在 $x$ 的子树中。由于小偷和警察是足够聪明的,对每种小偷的分布情况,警察会选择最优方案。那么小偷要做的就是使警察的最优方案尽可能大。

我们令 $g[x]$ 表示当前处理了一些子树,共分配了 $x$ 个小偷时,能使警察的最优方案需要花费的最劣时间。

则枚举 $v$ 的子树 $t$($t\neq u$),再枚举分配的小偷个数。则:

这里 $\min$ 表示警察使自己的方案尽可能优,$\max$ 表示小偷使警察的方案尽可能劣。

最后 $f[u][v][x][y]=g[x]$,是最优决策。

求 $f$ 的时候使用记忆化搜索即可。

状态总数为 $n^3$,每次转移 $O(n^2)$,所以时间复杂度 $O(n^5)$,远远跑不满,可以通过。

Code:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=52;
int f[N][N][N][N],sz[N],n,m,head[N],cnt,s,deg[N];
struct edge{
    int to,nxt,w;
}e[N<<1];
void dfs(int now,int pre){
    for(int i=head[now];i;i=e[i].nxt)if(e[i].to!=pre)dfs(e[i].to,now),sz[now]+=sz[e[i].to];
}
int dp(int u,int v,int x,int y){
    int&F=f[u][v][x][y];
    if(F!=-1)return F;
    if(x==0&&y==0)return F=0;
    if(deg[v]==1)return F=dp(v,u,y,0)+(y?e[head[v]].w:0);
    int g[N];
    memset(g,0,sizeof g);
    g[0]=0x3fffffff;
    for(int i=head[v];i;i=e[i].nxt)
        if(e[i].to!=u){
            for(int s=x;s;--s)
                for(int t=s;t;--t)
                    g[s]=max(g[s],min(g[s-t],dp(v,e[i].to,t,x+y-t)+e[i].w));
        }
    return F=g[x];
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<n;++i){
        int u,v,w;
        cin>>u>>v>>w;++deg[u],++deg[v];
        e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
        e[++cnt]=(edge){u,head[v],w},head[v]=cnt;
    }
    cin>>s>>m;
    for(int x;m--;)
        cin>>x,++sz[x];
    memset(f,-1,sizeof f);
    dfs(s,0);
    int ans=2e9;
    for(int i=head[s];i;i=e[i].nxt)
        ans=min(ans,dp(s,e[i].to,sz[e[i].to],sz[s]-sz[e[i].to])+e[i].w);
    cout<<ans<<'\n';
    return 0;
}