参考博客:http://www.cnblogs.com/waldenlake/p/9750249.html
题意:将一棵n个点的带权有根树剖分成尽量少的链,使得
(1)链的两个端点是祖先关系
(2)链含有的顶点个数小于等于L
(3)链上所有点的点权和小于等于S。
求出最少链的数量,如果无解输出-1。N<=105
思路:
想法非常的新颖 ,根据子节点的情况来决定父节点是跟随某个子节点继续向上,还是自己独立开始一段新的path。
但是!!!想想这个问题的性质,有个疑问:局部最优会导致全局最优吗?答案是肯定的,所以,直接上贪心!
我们来看这个贪心的性质,就是有两个叶子节点a,b,他们有公共的父节点c,沿着a,b一直向上寻找(不同问题有不同寻找方向,一般从下网上,但别被拘束),直到长度或者w超限,假设a能到c上面的一个节点,b却能到c上面的三个节点,那么这三个节点划分到b的path为最优,因为我总不至于能包含却不去包含他们的。
就是说,即便先选了a,向上追溯并染色到了c上1点,后选了b,向上追溯并染色到了c上3点,我们不就是等于说,a的后半段不跟a了,改成属于b的path。因为我们总是改变后半段的归属,右总是从叶子节点开始,所以这种贪心总是能得到更好的结果,而且保证最好的结果是总能出现的。
下面的代码是可以hack的,因为没有保证每次都是从叶子节点开始寻找,只要做一次bfs对层次作排序就能保证son总是出现father后
\没想到div2最后一题居然这么简单的贪心就可以解决。。还是思维不够@#$%
#include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back #define mp make_pair #define fi first #define se second #define pii pair<int,int> #define all(v) v.begin(),v.end() const int N = 1E5+3; ll w[N],p[N]; int vis[N]; int main(){ ll n,l,s; cin>>n>>l>>s; int f=0; for(int i=1;i<=n;++i){ cin>>w[i]; if(w[i]>s)f=1; } for(int i=2;i<=n;++i)cin>>p[i]; if(f){ printf("-1\n");return 0; } int ans= 0 ; //其实还不太准确 要保证从叶子开始 for(int i=n;i>=1;--i){ if(vis[i]==0){ ans++; int j = i; ll xl =l,xs= s; while(xs >= w[j] && xl>0 && j!=0){ xs -= w[j]; vis[j]=1; xl--; j = p[j]; } } } cout<<ans<<endl; return 0; }