Codeforces Round #438 by Sberbank and Barcelona Bootcamp (Div. 1 + Div. 2 combined) E - Policeman and a Tree

\(dp[p][u][s_0][tol]\) 表示警察从 \(p\) 走到 \(u\)\(u\) 相对于 \(p\) 的子树内有 \(s_0\) 个犯人,全局总共有 \(tol\) 个犯人的最小花费时间。
\(tol = 0\) 时,花费为 \(0\)
\(s_0=0\) 时,花费为 \(\text{INF}\),因为这一步是没有意义的。
\(u\) 为叶子时,\(dp[p][u][s_0][tol]=dp[u][p][tol-s_0][tol-s_0]+cost(p,u)\),走进这个叶子抓完犯人再往回走即可。
否则就类似于树形背包的转移
\(f[i][j]\) 表示解决完前 \(i\) 棵子树里共 \(j\) 个犯人的花费。
\(f[i][j+k] = \max\{f[i][j+k],\min\{f[i-1][j],dp[u][v][k][tol]\}\}\)
警察会选择DP值最小的走,而犯人会选择最大的方式来分布它们的位置,这是外层取 \(\max\) 内层取 \(\min\) 的原因,记忆化搜索。
因为 \(f\) 是共用的,那么转移前先调用一遍再转移。

#include 

const int N = 55;
const int INF = 0x3f3f3f3f;
int dp[N][N][N][N], f[N][N];
int n, root, sz[N], deg[N], val[N][N];
std::vector adj[N];

void dfs(int u, int f) {
    for (int i = 0; i < adj[u].size(); i++) {
        int v = adj[u][i];
        if (v == f) continue;
        dfs(v, u);
        sz[u] += sz[v];
    }
}

int DP(int p, int u, int s0, int tol) {
    if (!tol) return dp[p][u][s0][tol] = 0;
    if (!s0) return dp[p][u][s0][tol] = INF;
    if (dp[p][u][s0][tol] < INF) return dp[p][u][s0][tol];
    if (deg[u] == 1) return dp[p][u][s0][tol] = DP(u, p, tol - s0, tol - s0) + val[p][u];
    for (int i = 0; i < adj[u].size(); i++) {
        int v = adj[u][i];
        if (v == p) continue;
        for (int j = 0; j <= s0; j++)
            DP(u, v, j, tol);
    }
    for (int i = 0; i < 2; i++)
        for (int j = 0; j <= s0; j++)
            f[i][j] = 0;
    f[0][0] = INF;
    int cur = 1, last = 0;
    for (int i = 0; i < adj[u].size(); i++) {
        int v = adj[u][i];
        if (v == p) continue;
        memset(f[cur], 0, sizeof(f[cur]));
        for (int j = 0; j <= s0; j++)
            for (int k = 0; j + k <= s0; k++)
                f[cur][j + k] = std::max(f[cur][j + k], std::min(f[last][j], dp[u][v][k][tol]));
        std::swap(cur, last);
    }
    return dp[p][u][s0][tol] = f[last][s0] + val[p][u];
}

int main() {
    scanf("%d", &n);
    for (int i = 1, u, v, w; i < n; i++) {
        scanf("%d%d%d", &u, &v, &w);
        adj[u].push_back(v); adj[v].push_back(u);
        val[u][v] = val[v][u] = w;
        deg[u]++; deg[v]++;
    }
    scanf("%d", &root);
    int m;
    scanf("%d", &m);
    for (int i = 1, u; i <= m; i++) {
        scanf("%d", &u); 
        sz[u]++;
    }
    dfs(root, 0);
    memset(dp, 0x3f, sizeof(dp));
    int ans = INF;
    for (int i = 0; i < adj[root].size(); i++) {
        int v = adj[root][i];
        ans = std::min(ans, DP(root, v, sz[v], m));
    }
    printf("%d\n", ans);
    return 0;
}

你可能感兴趣的:(Codeforces Round #438 by Sberbank and Barcelona Bootcamp (Div. 1 + Div. 2 combined) E - Policeman and a Tree)