参考博客: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; }