HDU 4616 Game (树形DP+枚举思维方法)*

Game

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 2520    Accepted Submission(s): 857


 

Problem 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

 

 

Author

SYSU

 

 

Source

2013 Multi-University Training Contest 2

 

 

Recommend

zhuyuanchen520   |   We have carefully selected several similar problems for you:  6447 6446 6445 6444 6443 

 

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long
/*
题目大意:给定一棵树,树上每个节点都有权重,
并且有些有陷阱。求符合条件的最长链,注意,这里条件也有坑点。

首先思考如果不加限制会如何呢?
仅要求要找一条权重最长的链,
除了用特殊方法(两次广搜)之外如何把最长的长度枚举到?
我们追寻答案不就是要让极值出现在我们的枚举范围之内嘛。

我一开始的想法很复杂,
我先用一个Dp数组表示当前节点一直朝下的路径,
然后还考虑这个路径不仅要朝下,还可以朝上延伸,
还想者配合个朝上的DP然后二次DFS(虽然这样还是有问题)。

枚举的奥妙来了,其实分析一下,
任意的一条答案路径,都可以看成是不超过两条一直朝下的路径组成的。
那么我们只要一个dp数组就行了。
然后任意两条这样的路径都可以拟定个顺序,
这样就可以利用dfs子节点延展的无后效性来进行枚举。
就是利用当前枚举到的链和之前计算好的链可以把答案完整的搜索出来。

现在思考限制,难点就是限制不完全是数学上的不大于等于k就行了,
而是在达到k次时恰好停止。
这样dp数组我们要考虑三维,多出的一维用来区别起点是否有陷阱。
第二维如何利用应该很明显了,只要在答案允许的范围内枚举更新即可,
注意子情况,不能从dp[i][0][1]转移到任何状态。

最后就是答案的筛选过程了,
答案总共有四种情况,
0,0,
0,1,
1,0
1,1
这四种筛选时是有区分的,
当一端已经有k次后是不能转移到另一端的。
具体的看代码,代码有参考部分大神的。
*/

const int  maxn =6e4+5;
const int mod=1e9+7;
///链式前向星
struct node{int u,nxt;};
node edge[maxn<<1];
int head[maxn],tot=0;
int val[maxn],mark[maxn];
int dp[maxn][5][2];///起点为0或者为1,u子树中到达根节点的最大权重。
void init()
{
    memset(dp,0,sizeof(dp));
    memset(head,-1,sizeof(head));
    tot=0;
}
void add(int x,int y)
{
    edge[tot]=node{y,head[x]};
    head[x]=tot++;
}

int n,k,x,y;
int ans=0;
void dfs(int u,int pre)
{
    dp[u][mark[u]][mark[u]]=val[u];
    for(int i=head[u];~i;i=edge[i].nxt)
    {
        int v = edge[i].u;
        if(v==pre) continue;
        dfs(v,u);
        for(int j=0;j<=k;j++)
            for(int p=0;p+j<=k;p++)
            {
                if(j!=k&&p>0) ans=max(ans,dp[u][j][0]+dp[v][p][1]);///枚举两部分树链的情况
                if(p!=k&&j>0) ans=max(ans,dp[u][j][1]+dp[v][p][0]);
                if(j+p<k) ans=max(ans,dp[u][j][0]+dp[v][p][0]);
                 ans=max(ans,dp[u][j][1]+dp[v][p][1]);
            }
        for(int j=0;j+mark[u]<=k;j++)
        {
            if(dp[u][j+mark[u]][0]<dp[v][j][0]+val[u])
                dp[u][j+mark[u]][0]=dp[v][j][0]+val[u];
        }
        for(int j=1;j+mark[u]<=k;j++)
        {
            if(dp[u][j+mark[u]][1]<dp[v][j][1]+val[u])
                dp[u][j+mark[u]][1]=dp[v][j][1]+val[u];
        }
    }
}

int main()
{
    int t ;scanf("%d",&t);
    while(t--)
    {
        init();   scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf( "%d%d",&val[i] , &mark[i] );
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            add(x+1,y+1);  add(y+1,x+1);
        }
        ans=0;
        dfs(1,-1);
        printf("%d\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值