您的位置:首页 > 其它

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

2015-08-30 16:27 344 查看
题目链接:传送门 

题意:

给定一棵根为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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: