树上的石头

原题:https://cn.vjudge.net/problem/TopCoder-14812

http://210.33.19.103/contest/1002/problem/4

树上的石头

时间:1s   空间:256M

题目描述:

给你一棵树,标号为0到n-1,0为根节点,每个点有点权
现在你可以在一些点上放石头,也可以拿掉某些点上的石头
一个点可以放石头当且仅当这个点的所有儿子都放上了石头
如果根节点放上石头任务完成
求在整个过程中放着石头的节点的点权之和的最大值的最小值( 也就是说你要选择一个合理的顺序放置石头来使得答案最优)

输入格式:

第一行输入一个整数$n$ ($2 \le n \le 1000$)

第二行输入$n-1$个整数$p[0]->p[n-2]$, $p[i]$表示$(i+1)$与$p[i]$之间有一条边,$0 \le p[i] \le i$

第三行输入n个整数表示每个点的点权,$1 \le w[i] \le 10^5$ , w非减

输出格式:

输出一个整数

样例输入1:

5
0 1 2 3
1 2 2 4 4

样例输出1:

8 

样例输入2:

5
0 0 0 0 
1 2 3 4 5

样例输出2:

15

样例一解释:

{0,1,2,3} //分别表示1的父亲 2的父亲 3的父亲 4的父亲
{1,2,2,4,4}//每个点的点权值
Returns: 8
五个节点构成了一条链
在节点4上放一个石头 (权值和 = 4).
在节点3上放一个石头 (权值和 = 8).
移除节点4上的石头   (权值和 = 4).
在节点2上放一个石头 (权值和 = 6).
在节点1上放一个石头 (权值和 = 8).
移除节点2上的石头   (权值和 = 6)
在节点0(根节点)上放一个石头 (权值和 = 7)
整个过程中最大的权值和为8,不存在比最大值比8小的方案了

自己多造数据

子任务一30分:n<=20

子任务二30分:n<=100

子任务三40分:n<=1000


可能是常见贪心模型?感觉见过?然而不会

找不到是哪里的模型了..好像跟蓝书前几页的一个经典贪心题很像?

设f[i]=dfs(i),表示i节点为根子树的答案;d[i]表示i自身权值
对于某个节点u来说,在确定其各个子节点的答案之后,实际上就是要确定放其子树中点的顺序
放u某个子节点v的实际代价,是f[v]+(之前已经放好的u的子节点的权值(d)之和)
错解:把子节点按f值排序,大的在前;由于可能导致某时刻d值和太大,导致不优
正解:
如果一种方案的顺序有i恰好在j之前,且f[i]-d[i]<f[j]-d[j]
则设放i时之前的d值和为sum
则放i,j时代价为max(f[i],f[j]+d[i])+sum;交换i,j,代价变为max(f[j],f[i]+d[j])+sum
可以按f[i]与f[j]大小关系分类讨论,证明交换i,j后一定不会变得更劣(即不会变得更大)
因此按照f[i]-d[i]从大到小排序即可

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<vector>
 5 #include<functional>
 6 using namespace std;
 7 #define fi first
 8 #define se second
 9 #define mp make_pair
10 #define pb push_back
11 typedef long long ll;
12 typedef unsigned long long ull;
13 typedef pair<int,int> pii;
14 int n;
15 vector<int> ch[1010];
16 int d[1030];
17 struct
18 {
19     bool operator()(const pii &a,const pii &b)
20     {
21         return a.fi-a.se>b.fi-b.se;
22     }
23 }cmp;
24 int dfs(int u)
25 {
26     vector<pii> tmp;
27     for(auto v:ch[u])
28     {
29         tmp.pb(mp(dfs(v),d[v]));
30     }
31     sort(tmp.begin(),tmp.end(),cmp);
32     int sum=0,an=0;
33     for(int i=0;i<tmp.size();i++)
34     {
35         an=max(an,tmp[i].fi+sum);
36         sum+=tmp[i].se;
37     }
38     //printf("1t%d %d\n",u,an);
39     return max(an,sum+d[u]);
40 }
41 int main()
42 {
43     int i,b;
44     scanf("%d",&n);
45     for(i=2;i<=n;i++)
46     {
47         scanf("%d",&b);b++;
48         ch[b].pb(i);
49     }
50     for(i=1;i<=n;i++)    scanf("%d",&d[i]);
51     printf("%d",dfs(1));
52     return 0;
53 }
View Code

 

posted @ 2018-09-21 15:06  hehe_54321  阅读(217)  评论(0编辑  收藏  举报
AmazingCounters.com