【问题描述】
有一个古老的石头游戏,该游戏基于任意一棵树T,游戏的目标是在树T的根节点上放一颗石头,游戏的规则如下:
1、 游戏开始前,玩家先将K个石头放入桶中。
2、 在游戏的每一步,玩家从桶中拿一颗石头放到树的一个空的叶子节点上。
3、 当一个节点p的所有r个子节点都有一个石头,则移去r个子节点上的石头,然后将一个石头放到节点p上。剩下的r-1个石头重新放到桶中重复使用。
如果玩家根据以上规则放置石头,并最终在根节点上放置一颗石头,则赢得游戏。
现在的任务是求出游戏开始时,石头数K的最小值,使得玩家能在给定树的情况下赢得游戏。
【输入格式】
第一行输入测试用例的个数T,每个测试用例是一颗树的描述。第二行开始输入每棵树的描述。
每棵树有N个节点,节点标号为1,2,…N,每个节点可以有任意个子节点,根节点标号为1,。树的描述第一行为节点数N,第二行开始按照节点标号顺序描述N个节点的子节点,每行第一个数为标号p,第二个数为子节点数r,如果r不为0,接下来为r个子节点的标号。
【输出格式】
对每棵树,输出石头数的最小值。
【输入样例】
2
7
1 2 2 3
2 2 5 4
3 2 6 7
4 0
5 0
6 0
7 0
12
1 3 2 3 4
2 0
3 2 5 6
4 3 7 8 9
5 3 10 11 12
6 0
7 0
8 0
9 0
10 0
11 0
12 0
【输出样例】
3
4
【数据范围】
1<=T<=10 N<200
题意:
给出一棵一般的树,按如下的三种规则在树上放石子:
1,在游戏开始时,玩家可以拿K个石子放在水桶里。
2,在游戏的每一步,玩家可以从水桶里拿出一个石子,并放在任意一个空的叶子上。
3,当一个父节点的r个子节点都被放上了一个石子,可以将这r个石子都拿去并在父节点上放一个石子。水桶里石子可以再次使用。
显然,最终根上会被放上一个石子,问的是,求用最少的石子达到上述要求,求这个最小值。(有很多k,求最小的那个);
思路:
设所求结果用result来表示,我们要求的是以1为根的result,这个结果必定依赖于它的子节点的相关信息。这个信息会是什么呢?
考虑一棵树的子孙是该树的子树。假设1的子孙分别为r1,r2,r3。那么r1,r2,r3,都是1的子树的根。假设以r1,r2,r3为根的上述结果分别为result1,result2,result3,显然它们之和一定能达到1上也能被放上石子的目的。那这个值是不是最小的呢?假设result1>=result2>=result3,我们用result1个石子让r1上放上了一个石子,此时我们剩余了result1-1个石子没有用,这些石子可以弥补result2,最终可能会得到一个更小的值(<=result1+result2+result3,通过对先前石子的重复利用)。如何安排这种弥补方式呢?
最大值优先。r1需要的石子result1最大,那么最终的结果必定result>=result1,然而最终只需在r1上放一个石子,留下了result1-1个石子。
那么r1 ,r2一共需要Result = result1+result2-min( result-1,result2 )个石子,其中min(result1-1,result2)被加了两次,所以要减掉一次。
这样r1,r2就合并成了一个节点,最少需要Result个石子,那么result=Result+result3-min(Result-2,result3)。最终得解。
显然这是一个贪心算法,符合贪心选择和最优子结构
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<cstring>
#define mod 10007
#define maxn 505
#define oo 100000000
using namespace std;
int n,T;
int sz[maxn],d[maxn],g[maxn][maxn];
void read(int &x){
x=0;
bool ok=0;
char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
while((ch>='0'&&ch<='9')||ch=='-')
{
if(ch=='-') ok=1;
else x=x*10+ch-'0';
ch=getchar();
}
if(ok) x=-x;
}
void dfs(int i)
{
if(sz[i]==0)
{
d[i]=1;
return ;
}
int cnt=0,a[300];
for(int k=1;k<=sz[i];k++)
{
int j=g[i][k];
dfs(j);
a[++cnt]=d[j];
}
sort(a+1,a+cnt+1);
int num=a[cnt]-1,sum=a[cnt];
for(int k=cnt-1;k>=1;k--)
if(a[k]>num) sum+=(a[k]-num);
d[i]=sum;
}
void task()
{
dfs(1);
printf("%d\n",d[1]);
}
void in()
{
read(T);
while(T--)
{
read(n);
for(int i=1;i<=n;i++)
{
int excuse_me;
read(excuse_me);read(sz[excuse_me]);
for(int j=1;j<=sz[excuse_me];j++) read(g[excuse_me][j]);
}
task();
}
}
int main()
{
freopen("in.txt","r",stdin);
in();
return 0;
}