UVALive 5088 Alice and Bob's Trip(树形DP)

题目大意:给你一棵有n个点的树,根节点编号0,现在Alice 和 Bob要去旅行,Alice 想要边的和尽可能小,Bob想要尽量大,但是两个人都必须要使这个和在区间 [ l , r ],每人轮流选一条边,Bob 开始选,问你最大的和。

思路:树形DP,设d[ u ][ 0 ] 表示以u为根节点的子树,先有 Alice 选的最大和,d[ u ][ 1 ] 表示 Bob 先选的最大和,由于树中从根节点到任意一点的路径是唯一的,d[ u ][ 0 ] 获得的值其实是d[ u ][ 0 or 1 ] + dis(root , u),转移时要保证这个值要在 [ l ,  r ] 区间内,状态转移方程为 d[ u ][ 0 ] = min(d[ v ][ 1 ] + val( u, v )),d[ u ][ 1 ] = max(d[ v ][ 0 ] + val( u , v) ) ,同时满足前面说的区间的条件。

自己想的时候,状态表示、状态方程是挺简单的,可就是那个区间不好处理,我知道如果 d[ u ] > l 了,那么它就肯定不能去转移了,因为上面还有权值,那么 r 这里也不好处理,还有 就算 满足 d[ u ] <= l 了,也不能确定转移,因为上面的加起来可能会爆 r ,于是就没有办法了。。。 后来一看别人博客,他刚提到记录根节点到这个节点的距离和,这才恍然大悟,路径是唯一的呀,马上敲完,1A了。。。  唉,总是差一点,差一点。。。 = =

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF = 0x0fffffff;

const int MAXN = 500055;

int n,l,r;

struct Edge
{
    int t,next,val;
}edge[MAXN];

int tot,head[MAXN];

void add_edge(int s,int t,int val)
{
    edge[tot].t = t;
    edge[tot].val = val;
    edge[tot].next = head[s];
    head[s] = tot++;
}

int dis;

int check(int x)
{
    if( x <= r && x >= l) return 1;
    else return 0;
}

int d[MAXN][2];

void dfs(int u)
{
    d[u][0] = d[u][1] = 0;
    for(int e = head[u];e != -1;e = edge[e].next)
    {
        int v = edge[e].t;
        int val = edge[e].val;
        dis += val;
        dfs(v);
        dis -= val;
        int tmp = dis + val + d[v][1];
        //printf("tmp1 = %d\n",tmp);
        if(check(tmp))
            d[u][0] = min(val + d[v][1],(d[u][0] == 0 ? INF:d[u][0]));
        tmp = dis + val + d[v][0];
        //printf("tmp2 = %d\n",tmp);
        if(check(tmp))
            d[u][1] = max(val + d[v][0],d[u][1]);
    }
    //printf("u = %d,d0 = %d,d1 = %d\n",u,d[u][0],d[u][1]);
}

int main()
{
    while(~scanf("%d%d%d",&n,&l,&r))
    {
        tot=0;
        memset(head,-1,sizeof(head));
        for(int i = 1;i<n;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add_edge(a,b,c);
        }
        dis = 0;
        dfs(0);
        if(d[0][1] == 0) puts("Oh, my god!");
        else printf("%d\n",d[0][1]);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值