您的位置:首页 > 其它

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

2016-05-21 22:05 453 查看
树链剖分找权值最大的前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
;
vector<int>vec
;
int n,m;
int vis
;
LL val
;
LL a
;

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