POJ-1694(贪心)

来源:互联网 发布:淘宝卖的军用水壶真假 编辑:程序博客网 时间:2024/05/16 00:53

题目:http://poj.org/problem?id=1694

容易想到,从树上取下来的石子可以在别的子树上使用,对以X为根的子树进行Mark,假设X有N个孩子,则N个孩子必须都挂上石子之后,X才能被挂上石子,这个对这N个孩子挂石子的过程中,如果第i个孩子没有挂上石子的时候,就试图去挂j,则肯定不会优于先挂完i再去挂j,毕竟同时进行不会要求更少的总石子数,所以我们可以先挂上一个孩子,再去挂另外一个孩子,使得要求的石子数最少。


但问题是,那到底先从哪个孩子下手呢,这N个孩子已什么样的顺序去处理呢?知觉告诉我们,先挂要求总石子数多的,因为这样再去挂少的就甚至可以不用增加了总石子数了。假设一个处理顺序中,这N个孩子挂上石子分别需要总数为a[1]~a[N]的石子,且存在i,有a[i] < a[i+1],并假设在挂i之前需要的一开始的总数为T。

考虑开始挂i之前,我们能复用的前面剩下来的石子数,假设为R,

(1)R > a[i+1],则需要的总数为T,还剩R-2个可以用

(2)a[i] < R <= a[i+1],则需要的总数变为T+a[i+1]-R+1,还剩a[i+1]-1个可以用

(3)R <= a[i],则需要的总数变为T+a[i+1]-R+1,还剩a[i+1]-1个可以用

现在,如果我们反过来先挂a[i+1]再挂a[i],看看会是什么结果:

(1)R > a[i+1],则需要的总数为T,还剩R-2个可以用

(2)a[i] < R <= a[i+1],则需要的总数变为T+a[i+1]-R,还剩a[i+1]-2个可以用

(3)R <= a[i],则需要的总数变为T+a[i+1]-R,还剩a[i+1]-2个可以用

可以看到,先挂a[i],我们可能会在一开始的总数上多需要一个石子,然后这个石子继续在挂下一个子树的时候使用,所以i不能是N-1,否则,多出来的这个石子不会在为挂i+2时起作用,只会让结果变差,所以必须有a[N-1] >= a[N]。


那i可不可以是N-2呢,即会不会有a[N-2] < a[N-1] >= a[N]这样的情况呢,在上面(2)(3)的情况下继续分析:

(1)如果有a[N-1]=a[N],则无论先N-2还是N-1,需要的都是T+a[N-1]-R+2

(2)如果有a[N-1]=a[N]+1,则无论是先N-2还N-1,需要的都是T+a[N-1]-R+1

(3)如果有a[N-1]>=a[N]-2,则先选N-1需要T+a[i+1]-R,而先选N-2需要T+a[i+1]-R+1,先选N-1优于先选N-2

所以我们需要有a[N-2]>=a[N-1}的顺序以保证最优。


类似地分析,我们可以得到a[N-3] < a[N-2] >=a N-1] >= a[N]的顺序不会优于且有可能差于a[N-3] >= a[N-2] >=a[N-1] >= a[N]的顺序,以此类推,最后我们有,任一顺序不会优于且可能差于a[1]>=a[2]>=a[3]>=...>=a[N]的顺序。

所以,在知道孩子子树需要的总数值后,从大到小排序,不够就补,便可以得到根子树需要的最少总数值了。


#include <cstdio>#include <vector>#include <algorithm>using namespace std;#define MAX_N205int N;vector<int> children[MAX_N];vector<int> childRes[MAX_N];int mark(int x){if(children[x].empty()) return 1;vector<int>& c = children[x];vector<int>& r = childRes[x];r.resize(c.size());for(int i = 0; i < c.size(); ++i) r[i] = mark(c[i]);sort(r.begin(), r.end());int n = 0, t = 0;for(int i = r.size() - 1; i > -1; --i){if(n >= r[i]) --n;else{t += r[i] - n;n = r[i] - 1;}}return t;}int main(){int test, i, p, cnt, c;for(scanf("%d", &test); test--; ){scanf("%d", &N);for(i = 1; i <= N; ++i) children[i].clear();for(i = 1; i <= N; ++i){scanf("%d%d", &p, &cnt);while(cnt--){scanf("%d", &c);children[p].push_back(c);}}printf("%d\n", mark(1));}return 0;}


0 0