HDU 5242 Game(贪心)
http://acm.hdu.edu.cn/showproblem.php?pid=5242
题意:
给出一棵树,每个节点都有一个权值,每次可以获得从根结点(1)到叶子节点上的所有权值和,每个节点只能获得一次。求k次操作后可以获得的最大权值和。
思路:
反向建图,首先求出所有节点到根节点的权值和,然后降序排序,从可以获得最大权值和的叶子节点开始计算。已经遍历过的节点就直接返回0。计算完后再次重新排序,取前面的k个较大值。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 using namespace std; 6 const int maxn = 1e5+5; 7 typedef long long ll; 8 int tot, n, k; 9 ll val[maxn],head[maxn]; 10 bool vis[maxn]; 11 12 struct node 13 { 14 int v,next; 15 }e[maxn]; 16 17 struct Node 18 { 19 int id; 20 ll sum; 21 bool operator< (const Node& rhs)const 22 { 23 return sum>rhs.sum; 24 } 25 }t[maxn]; 26 27 void addEdge(int u, int v) 28 { 29 e[tot].v = v; 30 e[tot].next = head[u]; 31 head[u] = tot++; 32 } 33 34 ll dfs1(int u) 35 { 36 if(vis[u]) return t[u].sum; 37 vis[u] = true; 38 t[u].sum = val[u]; 39 for(int i=head[u];i!=-1;i=e[i].next) 40 { 41 int v = e[i].v; 42 t[u].sum += dfs1(v); 43 } 44 return t[u].sum; 45 } 46 47 ll dfs2(int u) 48 { 49 if(vis[u]) return 0; 50 vis[u] = true; 51 ll tmp = val[u]; 52 for(int i=head[u];i!=-1;i=e[i].next) 53 { 54 int v = e[i].v; 55 tmp += dfs2(v); 56 } 57 return tmp; 58 } 59 60 int main() 61 { 62 //freopen("in.txt","r",stdin); 63 int T; 64 int cas = 0; 65 scanf("%d",&T); 66 while(T--) 67 { 68 tot = 0; 69 memset(head,-1,sizeof(head)); 70 scanf("%d%d",&n,&k); 71 for(int i=1;i<=n;i++) scanf("%lld",&val[i]); 72 for(int i=1;i<n;i++) 73 { 74 int u,v; 75 scanf("%d%d",&u,&v); 76 addEdge(v,u); 77 } 78 memset(vis,0,sizeof(vis)); 79 memset(t,0,sizeof(t)); 80 for(int i=1;i<=n;i++) 81 { 82 t[i].id = i; 83 t[i].sum = dfs1(i); 84 } 85 sort(t+1,t+n+1); 86 memset(vis,0,sizeof(vis)); 87 for(int i=1;i<=n;i++) 88 { 89 int root = t[i].id; 90 t[i].sum = dfs2(root); 91 } 92 sort(t+1,t+n+1); 93 ll ans = 0; 94 for(int i=1;i<=k;i++) 95 ans += t[i].sum; 96 printf("Case #%d: %lld\n",++cas,ans); 97 } 98 return 0; 99 }