• HDU 6662 Acesrc and Travel (换根dp)


    Problem Description
    Acesrc is a famous tourist at Nanjing University second to none. During this summer holiday, he, along with Zhang and Liu, is going to travel to Hong Kong. There are n spots in Hong Kong, and n1 bidirectional sightseeing bus routes connecting these spots. They decide to visit some spots by bus.

    However, Zhang and Liu have different preferences for these spots. They respectively set a satisfactory value for each spot. If they visit the ith spot, Zhang will obtain satisfactory value ai, and Liu will obtain bi. Starting with Zhang, they alternately decide the next spot to visit for the sake of fairness. There must be a bus route between the current spot and the next spot to visit. Moreover, they would never like to visit a spot twice. If anyone can't find such a next spot to visit, they have no choice but to end this travel.

    Zhang and Liu are both super smart competitive programmers. Either want to maximize the difference between his total satisfactory value and the other's. Now Acesrc wonders, if they both choose optimally, what is the difference between total satisfactory values of Zhang and Liu?
     
    Input
    The first line of input consists of a single integer T (1T30), denoting the number of test cases.

    For each test case, the first line contains a single integer n (1n105), denoting the number of spots. Each of the next two lines contains n integers, a1,a2,,anand b1,b2,,bn (0ai,bi109), denoting the 
    satisfactory value of Zhang and Liu for every spot, respectively. Each of the last n1 lines contains two integers x,y (1x,yn,xy), denoting a bus route between the xth spot and the yth spot. It is reachable from any spot to any other spot through these bus routes.

    It is guaranteed that the sum of n does not exceed 5.01×105.
     
    Output
    For each test case, print a single integer in one line, the difference of total satisfactory values if they both choose optimally.
     
    Sample Input
    1 3 1 1 1 0 2 3 1 2 1 3
     
    Sample Output
    -1
     
    题意:
    给定一棵树,每个节点上对应两个权值,分别是两个人的满意度,他们两个都想让自己的满意度减去另外一个人的满意度之和尽可能大,求最终得到的值.
    注意,这两个人交替选择方向,并且不能走回头路,由第一个人选择起始点.
    (题意太复杂,说得不好请见谅)
    思路:
    树形dp,求出以1号节点为起始点时,每个点由第一个人和第二个人选择而来得到的最大值和次大值.
    然后换根即可.
    其中有很多地方需要注意,如如果一个点只有一个儿子结点或者本身是叶子结点时,需要特殊处理一下,因为在换根途中,切断其与父亲结点的关系之后,其最大值和最小值可能存在问题.
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<map>
    #include<set>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<ctime>
    
    #define fuck(x) cerr<<#x<<" = "<<x<<endl;
    #define debug(a, x) cerr<<#a<<"["<<x<<"] = "<<a[x]<<endl;
    #define lson l,mid,ls
    #define rson mid+1,r,rs
    #define ls (rt<<1)
    #define rs ((rt<<1)|1)
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int loveisblue = 486;
    const int maxn = 100086;
    const int maxm = 200086;
    const ll Inf = 0x3f3f3f3f3f3f3f3f;
    const int mod = 1000000007;
    const double eps = 1e-6;
    const double pi = acos(-1);
    
    int Head[maxn], cnt;
    struct edge {
        int Next, v;
    } e[maxm];
    
    void add_edge(int u, int v) {
        e[cnt].Next = Head[u];
        e[cnt].v = v;
        Head[u] = cnt++;
    }
    
    ll a[maxn];
    
    ll dp1[2][maxn];//0由第一个人选择而来,1第二个选择而来
    ll dp2[2][maxn];//
    bool vis[maxn];
    
    void dfs(int u, int fa) {
        bool flag = true;
        for (int k = Head[u]; k != -1; k = e[k].Next) {
            if (e[k].v == fa) {
                continue;
            }
            dfs(e[k].v, u);
            flag = false;
    
            ll tmp1 = dp1[1][e[k].v];
            ll tmp0 = dp1[0][e[k].v];
    
            //当前由第一个人选择而来的,那下一个一定由第二个人选择而来
            if (tmp1 < dp2[0][u]) { swap(tmp1, dp2[0][u]); }
            if (dp2[0][u] < dp1[0][u]) { swap(dp2[0][u], dp1[0][u]); }
    
            if (tmp0 > dp2[1][u]) { swap(tmp0, dp2[1][u]); }
            if (dp2[1][u] > dp1[1][u]) { swap(dp2[1][u], dp1[1][u]); }
        }
    
        vis[u] = flag;
        //叶子节点的特殊处理
        if (flag) {
            dp1[0][u] = 0;
            dp1[1][u] = 0;
        }
        if (dp1[0][u] != Inf) dp1[0][u] += a[u];
        if (dp1[1][u] != -Inf) dp1[1][u] += a[u];
        if (dp2[0][u] != Inf) dp2[0][u] += a[u];
        if (dp2[1][u] != -Inf) dp2[1][u] += a[u];
    }
    
    ll ans = -Inf;
    
    void dfs1(int u, int fa) {
    
        ll tmp0 = dp1[0][fa];
        ll tmp1 = dp1[1][fa];
    
    
        //如果父亲节点的最值是由u转移来的,那么就要利用次值换根
        if (tmp0 == dp1[1][u] + a[fa]) {
            tmp0 = dp2[0][fa];
        }
        if (tmp1 == dp1[0][u] + a[fa]) {
            tmp1 = dp2[1][fa];
        }
    
        //如果fa是只有u这一个儿子,并且fa==1时,才会出现这种情况
        if (tmp1 == -Inf) {
            tmp1 = a[fa];
        }
        if (tmp0 == Inf) {
            tmp0 = a[fa];
        }
        tmp0 += a[u];
        tmp1 += a[u];
    
    
        //u是叶子节点,直接特判
        if (vis[u]) {
            ans = max(ans, tmp1);
        }
        //此段代码和dfs中的完全相同
        if (tmp1 < dp2[0][u]) { swap(tmp1, dp2[0][u]); }
        if (dp2[0][u] < dp1[0][u]) { swap(dp2[0][u], dp1[0][u]); }
    
        if (tmp0 > dp2[1][u]) { swap(tmp0, dp2[1][u]); }
        if (dp2[1][u] > dp1[1][u]) { swap(dp2[1][u], dp1[1][u]); }
    
        ans = max(ans, dp1[0][u]);
    
        for (int k = Head[u]; k != -1; k = e[k].Next) {
            if (e[k].v != fa)dfs1(e[k].v, u);
        }
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("in.txt", "r", stdin);
    #endif
        int T;
        scanf("%d", &T);
        while (T--) {
            int n;
            ans = -Inf;
            cnt = 0;
            scanf("%d", &n);
            for (int i = 1; i <= n; i++) {
                Head[i] = -1;
                scanf("%lld", &a[i]);
                dp1[0][i] = dp2[0][i] = Inf;
                dp1[1][i] = dp2[1][i] = -Inf;
    
            }
            for (int i = 1; i <= n; i++) {
                ll x;
                scanf("%lld", &x);
                a[i] -= x;
            }
            for (int i = 1; i < n; i++) {
                int x, y;
                scanf("%d%d", &x, &y);
                add_edge(x, y);
                add_edge(y, x);
            }
            dfs(1, 0);
            ans = dp1[0][1];
            for (int k = Head[1]; k != -1; k = e[k].Next) {
                dfs1(e[k].v, 1);
            }
            printf("%lld
    ", ans);
        }
        return 0;
    }
    View Code

    对代码有问题可以留言

  • 相关阅读:
    Android RecyclerView如何去掉上拉刷新和下拉加载的阴影
    python如何在列表、对象、集合中根据条件筛选数据
    解决Glide在一个imageview上更换图片时会闪的问题
    响应时间三个非常重要的时间点
    android 向系统日历添加日程事件
    android 系统日历 插入重复事件规则 RRULE
    android Calendar Provider
    android edittext设置获取焦点
    android 自定义光标颜色
    android动态改变TextView字体大小遇到的问题
  • 原文地址:https://www.cnblogs.com/ZGQblogs/p/11355640.html
Copyright © 2020-2023  润新知