一本通 1774:大逃杀

Posted natsuka

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一本通 1774:大逃杀相关的知识,希望对你有一定的参考价值。

(quad) (n) 个点,(n - 1) 条边,显然就是一棵树了。题干说“可选择不获取资源”,但由于获取资源不需要时间,那显然必须要获取。
(quad) 发现数据范围比较小,考虑多维 dp。设计如下:
(qquad) (ullet)(dp_{i,j,k}) 表示从 (i) 出发,花费 (k) 单位时间到达 (j) 的最大收益。
(qquad) (ullet) (dp_{i,j,k} = dp_{i,p,t} + dp_{p,j,k-t})
(quad) 这样最多要转移 (2.73 imes 10^{12}) 次,显然超时。主要原因是状态设计得太过具体、暴力。
(qquad) (ullet)(f_{i,j}) 表示花费 (j) 单位时间,从 (i) 出发,经过 (i) 的子树,最终返回 (i) 的最大收益;(g_{i,j}) 表示花费 (j) 单位时间,从 (i) 出发,最终到达 (i) 的子树的最大收益;(h_{i,j}) 表示花费 (j) 单位时间,从 (i) 的子树中出发,经过 (i),最终到达 (i) 的子树的最大收益。
技术图片
(qquad) (ullet) 事实上,(g_{i,j}) 不但可以表示“花费 (j) 单位时间,从 (i) 出发,最终到达 (i) 的子树的最大收益”,还可以表示“花费 (j) 单位时间,从 (i) 的子树出发,最终到达 (i) 的最大收益”。
(quad) 上述说明中的所有出发和到达都指:消灭了出发至到达路径上的所有敌人。个人感觉这道题还是相当复杂的,转移的具体描述和配图以后再补,可以先看代码试着理解。

#include <cstdio>

inline int read(void){
    static int res; res = 0; static char ch; ch = std::getchar();
    while(ch < '0' || ch > '9')
        ch = std::getchar();
    while(ch >= '0' && ch <= '9')
        res = res * 10 + ch - 48, ch = std::getchar();
    return res;
}

inline int max(const int& a, const int& b){
    return a > b ? a : b;
}

const int MAXN = 3e2 + 19;

struct Edge{
    int to, next, dist;
}edge[MAXN << 1];

int cnt, head[MAXN];

inline void add(int from, int to, int dist){
    edge[++cnt].to = to;
    edge[cnt].dist = dist;
    edge[cnt].next = head[from];
    head[from] = cnt;
}

int n, T;
int w[MAXN], t[MAXN];
int a, b, c;
int f[MAXN][MAXN], g[MAXN][MAXN], h[MAXN][MAXN];
int ans;

void dfs(int node, int fa){
    static int tmp[3];
    if(t[node] >= T)
        return;
    for(int i = head[node]; i; i = edge[i].next)
        if(edge[i].to != fa){
            dfs(edge[i].to, node);
            for(int j = T; j >= t[node]; --j){
                for(int k = j - edge[i].dist; k >= t[node]; --k){
                    tmp[0] = f[node][k], tmp[1] = g[node][k], tmp[2] = h[node][k];
                    int rest = j - k - edge[i].dist;
                    if(rest >= t[edge[i].to]){
                        g[node][j] = max(g[node][j], tmp[0] + g[edge[i].to][rest]);
                        h[node][j] = max(h[node][j], tmp[1] + g[edge[i].to][rest]);
                    }
                    rest -= edge[i].dist;
                    if(rest >= t[edge[i].to]){
                        g[node][j] = max(g[node][j], tmp[1] + f[edge[i].to][rest]);
                        f[node][j] = max(f[node][j], tmp[0] + f[edge[i].to][rest]);
                        h[node][j] = max(h[node][j], tmp[2] + f[edge[i].to][rest]);
                        h[node][j] = max(h[node][j], tmp[0] + h[edge[i].to][rest]);
                    }
                }
                ans = max(ans, h[node][j]);
            }
        }
}

int main(){
    n = read(), T = read();
    for(int i = 1; i <= n; ++i)
        w[i] = read();
    for(int i = 1; i <= n; ++i){
        t[i] = read();
        if(t[i] <= T)
            f[i][t[i]] = g[i][t[i]] = h[i][t[i]] = w[i];
    }
    for(int i = 1; i < n; ++i){
        a = read(), b = read(), c = read();
        add(a, b, c), add(b, a, c);
    }
    dfs(1, 0);
    std::printf("%d
", ans);
    return 0;
}

(quad) 其实只要出现了树形结构,就要试着考虑树形 dp,不要再去想奇奇怪怪的转移方程了。树形 dp 的特点:
(qquad) (ullet) 状态与单个节点有关,每个节点的状态都一定可以由子节点转移。
(qquad) (ullet) 转移与 dfs 结合。
(quad) 在树形 dp 中,如果给定的图的边都是自上而下的,一定可以连有向边,最上面的点为根。反之,连无向边,随意选取根节点(有时不选 (1) 当根会被卡)。
本文系参考 1773:大逃杀 而成。

以上是关于一本通 1774:大逃杀的主要内容,如果未能解决你的问题,请参考以下文章

《大逃杀》人物关系图

《大逃杀》人物关系图

unit13——大逃杀,section two

“大逃杀”这么火,为何国内“绝地求生”都靠手游撑面子?

加密市场「大逃杀」:清算抛售挤兑

[51nod2982] 大逃杀