树上的石头
原题: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]从大到小排序即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }