zoukankan      html  css  js  c++  java
  • *hdu 4616 Game 树形DP

    Description

    Nowadays, there are more and more challenge game on TV such as 'Girls, Rush Ahead'. Now, you participate int a game like this. There are N rooms. The connection of rooms is like a tree. In other words, you can go to any other room by one and only one way. There is a gift prepared for you in Every room, and if you go the room, you can get this gift. However, there is also a trap in some rooms. After you get the gift, you may be trapped. After you go out a room, you can not go back to it any more. You can choose to start at any room ,and when you have no room to go or have been trapped for C times, game overs. Now you would like to know what is the maximum total value of gifts you can get.

    Input

    The first line contains an integer T, indicating the number of testcases.
    For each testcase, the first line contains one integer N(2 <= N <= 50000), the number rooms, and another integer C(1 <= C <= 3), the number of chances to be trapped. Each of the next N lines contains two integers, which are the value of gift in the room and whether have trap in this rooom. Rooms are numbered from 0 to N-1. Each of the next N-1 lines contains two integer A and B(0 <= A,B <= N-1), representing that room A and room B is connected.
    All gifts' value are bigger than 0.

    Output

    For each testcase, output the maximum total value of gifts you can get.

    Sample Input

    2 3 1 23 0 12 0 123 1 0 2 2 1 3 2 23 0 12 0 123 1 0 2 2 1

    Sample Output

    146 158

    题目大意:一颗n个点的树,每个点含有一个价值大于0的礼物,且其中有一些点会有陷阱,你有c次机会掉进陷阱,走到一个点就可以获得该点礼物值,走过的点不能再走。

    现在起始点任选,让你输出最大获取的礼物值。

    学习别人的思路

    定义:

    down[u][j]表示在从u结点出发往u的子节点方向,前方(包含u)共消耗j次机会所能获得的最大礼物值。

    up[u][j]表示从u的子节点方向来,最终到达u结点,共消耗j次机会所能获得的最大礼物值。

    状态转移方程:

    $$up[u][j+trap[u]] = max(up[u][j+trap[u]],up[v][j]+p[u]);$$

    $$down[u][j+trap[u]]=max(down[u][j+trap[u]],down[v][j]+p[u]);$$

    其中第二个式子要注意,当$j=0$的时候,down[v][j]的出现显然不符合题意,所以此时不更新。

    还有对ans的更新,具体看代码注释。

    #include <vector>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn = 50005;
    int n,c,ans;
    vector<int> adj[maxn];
    int trap[maxn];
    int p[maxn];
    int down[maxn][5],up[maxn][5];
    
    void dfs(int u,int fa) {
        up[u][trap[u]] = down[u][trap[u]] = p[u];
        int len = adj[u].size();
        for(int i=0;i<len;i++) {
            int v = adj[u][i];
            if(v==fa) continue;
            dfs(v,u);
            ///更新ans
            for(int j=0;j<=c;j++) 
            {
                for(int k=0;k+j<=c;k++)
                {
                    //这里用到的与u有关的up与down,都是限定于之前访问过的u的子树
                    //用到含有v的up与down,是当前v的子树
                    //这样处理就不会出现“上来”与“下去”是同一条边了,很巧妙
                    if(j!=c) //j=c必须停止,因为机会用完了,不能再下去了
                        ans=max(ans,up[u][j]+down[v][k]); 
                    if(k!=c) //同上
                        ans=max(ans,down[u][j]+up[v][k]); 
                    if(j+k<c) //只有当两条路机会总和小于k的时候才能把两条路合并
                        ans=max(ans,up[u][j]+up[v][k]);
                    //前两个if好理解,都是一上一下,这里为什么要加上第三个两个都是上的呢?
                    //是因为如果局限于一上一下,看上面的前两个if,就会发现这样会阻隔一些正确的
                    //路径,所以把两个向上的情况组合。
                }
            }
    
            ///更新 down[]  up[]
            for(int j=0;j<=c;j++)
            {
                up[u][j+trap[u]] = max(up[u][j+trap[u]],up[v][j]+p[u]);
                if(j!=0) 
                {
                    down[u][j+trap[u]]=max(down[u][j+trap[u]],down[v][j]+p[u]);
                }
            }
        }
    
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&c);
            for(int i=0;i<n;i++) {
                scanf("%d%d",&p[i],&trap[i]);
            }
            for (int i=1; i<n; i++)
            {
                int x,y;
                scanf("%d%d",&x,&y);
                adj[x].push_back(y);
                adj[y].push_back(x);
            }
            ans = 0;
            memset(down,0,sizeof(down));
            memset(up,0,sizeof(up));
            dfs(0,-1);
            printf("%d
    ",ans);
            for(int i=0;i<=n;i++) adj[i].clear();
        }
        return 0;
    }
  • 相关阅读:
    java中关于AtomicInteger的使用
    什么是程序的原子性
    ReadWriteLock ReentrantReadWriteLock
    InputStream转换为String, byte[] data = new byte[1024]详解
    Invalid argument during startup: unknown conf file parameter : requirepass
    ArrayBlockingQueue 阻塞队列和 Semaphore 信号灯的应用
    java并发之同步辅助类CyclicBarrier和CountDownLatch
    CSS学习摘要-定位
    CSS学习摘要-布局
    CSS学习摘要-浮动与清除浮动
  • 原文地址:https://www.cnblogs.com/lastone/p/5352823.html
Copyright © 2011-2022 走看看