HDU 5242 Game(2015年上海大都会G题)

题目链接:传送门 


题意:

给定一棵根为1的树,然后从根节点走到叶节点可以获得路径上所有点的权值和,每个点的权值只可以计算一次,就是走过一次以后再走这个点的话获得的权值就是0。可以走k次问k次获得的权值和最大为多少。


分析:

贪心的思想,预处理出每个节点到根节点的权值和,然后我们按照权值和的大小来排序,然后从权值和最大的那个节点开始选,然后选的过程把走过的点给标记,因为每次选择一个叶子结点走到根节点,相当于每次取一条单链,对于有交叉的两条链,先选权值大的肯定是最优的,因为对于某条跟它们没有交叉的链来说,这样子的操作并不会影响到它。


代码如下:

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

typedef long long LL;

const int maxn = 1e5+10;

LL a[maxn];

struct Nod{
    int id;
    LL sum;
    bool operator < (const struct Nod &tmp)const{
        return sum > tmp.sum;
    }
}nod[maxn];

struct graph{
    int head[maxn];
    int vis[maxn];
    int ip ;
    struct edge{
        int to,next;
    }edge[maxn];
    void init(){
        memset(head,-1,sizeof(head));
        ip=0;
    }
    void addedge(int u,int v){
        edge[ip].to=v;
        edge[ip].next=head[u];
        head[u]=ip++;
    }
    LL dfs1(int u){
        if(vis[u]) return nod[u].sum;
        vis[u]=1;
        nod[u].sum=a[u];
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v = edge[i].to;
            nod[u].sum+=dfs1(v);
        }
        return nod[u].sum;
    }
    LL dfs2(int u){
        if(vis[u]) return 0;
        vis[u]=1;
        LL w = a[u];
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v = edge[i].to;
            w+=dfs2(v);
        }
        return w;
    }
}G;

LL ans[maxn];

int main()
{
    int n,m,t,cas=1;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%I64d",&a[i]);
            nod[i].id=i;
        }
        G.init();
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            G.addedge(v,u);
        }
        memset(G.vis,0,sizeof(G.vis));
        for(int i=1;i<=n;i++){
            if(!G.vis[i]){
                G.dfs1(i);
            }
        }
        sort(nod+1,nod+n+1);
//        cout<<"|*****************|"<<endl;
//        for(int i=1;i<=n;i++)
//            cout<<nod[i].id<<" "<<nod[i].sum<<endl;
//        cout<<"|*****************|"<<endl;
        memset(G.vis,0,sizeof(G.vis));
        for(int i=1;i<=n;i++){
            ans[i]=G.dfs2(nod[i].id);
        }
        sort(ans+1,ans+n+1);
//        for(int i=1;i<=n;i++)
//            cout<<i<<" "<<ans[i]<<endl;
//        cout<<"|*****************|"<<endl;
        LL sum = 0;
        for(int i=n,j=0;i>0&&j<m;j++,i--){
            sum+=ans[i];
        }
        printf("Case #%d: %I64d\n",cas++,sum);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值