HDU 5242Game 树上的贪心 树形dp 求出使K条链的权值总和最大

57 篇文章 0 订阅
40 篇文章 0 订阅
树链剖分找权值最大的前k条链
题目大意:给定一个树形的游戏网络,可以从根节点出发k个人,每个人可以沿着一条路径走下去,不能回头,出口在各个叶子节点,在路过一个节点时可以
          获得该点的权值,每个点的权值只能被获得一次,问k个人怎样走最后可以获得的权值最多
解题思路:首先从反向建立一棵有向树(从叶子节点到根节点),首先dfs1找出每个节点到根节点的最大权路径,然后按权值递减排序,dfs2找每个点到根节点

          的最大权路径,走过的点不能重复走,最后在这些最大权路径中取前k大即为答案

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#define LL long long
#define INF 0x3f3f3f3f
#define mem(vis,a) memset(vis,a,sizeof(vis))
#define bug puts("**********");
#define N 100100

using namespace std;

struct node
{
    int id;
    LL sum;
    friend bool operator <(node p1,node p2)
    {
        if(p1.sum!=p2.sum)
         return p1.sum>p2.sum;
        return p1.id<p2.id;
    }
}p[N];
vector<int>vec[N];
int n,m;
int vis[N];
LL val[N];
LL a[N];

LL DFS(int x)    ///构建出树的个点到根的权值 ,减枝==记忆化搜索
{
    if(vis[x])return p[x].sum;
    p[x].sum=val[x];
    vis[x]=1;
    p[x].id=x;
    for(int i=0;i<vec[x].size();i++)
    {
        p[x].sum+=DFS(vec[x][i]);
    }
    return p[x].sum;
}
LL DFS2(int x)   
{
    if(vis[x])return 0;
    LL sum=val[x];
    vis[x]=1;
    for(int i=0;i<vec[x].size();i++)
    {
        sum+=DFS2(vec[x][i]);
    }
    return sum;
}
bool cmp(LL x,LL y)
{
    return x>y;
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++)
    {
        mem(vec,0);

        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        scanf("%lld",&val[i]);

        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            vec[y].push_back(x);   ///反向建树
        }
        mem(vis,0);
        for(int i=1;i<=n;i++)
        {
            if(!vis[i])   ///减枝
            DFS(i);
        }
        mem(a,0);    ///防止m>num 后遗留上次的算数值
        mem(vis,0);
        sort(p+1,p+n+1);

        int num=0;
        for(int i=1;i<=n;i++)
        {
           // if(!vis[p[i].id])  ///不要加这句话否则会使 m>num;
           // {
              a[num++]=DFS2(p[i].id);
              
            //}
        }

        sort(a,a+num,cmp);
        LL ans=0;
        for(int i=0;i<m;i++)
           ans+=a[i];
        printf("Case #%d: %lld\n",cas,ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值