一、问题描述及分析:
1.d森林:
设T为一带权树,树中的每个边的权都为整数。又设S为T的一个顶点的子集,从T中删除S中的所有结点,则得到一个森林,记为T/S。如果T/S中所有树从根到叶子节点的路径长度都不超过d,则称T/S是一个d森林。
2. 贪心策略:
(1)使用贪心的思想,从叶子节点向上遍历,每个节点的priceOfson记录该节点子节点中路径长度最大值,若priceOfson大于d则删除该节点,每一次遍历访问 无子节点或子节点均被访问或删除 且 未被标记 的节点,直到所有节点均被访问,得到最优解。
(2)设a为一节点,b1, b2 …… bi 为他的子节点,从叶子节点到bi的路径长度设为Li,当Li>d的字节点个数大于2时,删除a为更优解,当Li>d的个数为1时,删除子节点和a的个数相同,当Li > d的节点个数为0时,不删除节点。综上所述,删除高度更大的节点更符合贪心性质,可以得到更优解。
二、代码实现:
typedef struct node
{
int father; //父节点
int distance; //到父节点的路长
int out; //出度
int maxroad; //到叶节点的路畅
int cut; //切掉标志
}node, *pnode;
class dTree {
private:
pnode tree;
int BiggestDistance, numberofnode, sum, qhead, qend;
int que[1000];
public:
dTree(int n, int d) //构建与初始化树
{
sum = 0;
qhead = 0;
qend = 0;
BiggestDistance = d;
numberofnode = n;
tree = new node[n];
tree[0].father = -1;
tree[0].distance = 0;
for (int i = 0; i < n; i++)
{
int NumOfSon, WeightOfRoad, num;
cin >> NumOfSon ;
tree[i].cut = 0;
tree[i].out = NumOfSon ;
tree[i].maxroad = 0;
for (int j = 0; j < NumOfSon; j++)
{
cin >> num >> WeightOfRoad ;
tree[num].father = i;
tree[num].distance = WeightOfRoad;
}
}
}
void push(int i) //入队列
{
que[qend] = i;
qend++;
}
int pop() //出队列
{
int temp = que[qhead++];
return temp;
}
void solution()
{
for (int i = numberofnode - 1; i >= 0; i--)
{
if (tree[i].out == 0)
{
push(i);
}
}
while (qhead != qend)
{
int temp = pop();
int len = tree[temp].distance;
int par = tree[temp].father;
if (tree[par].cut == 0 && tree[temp].maxroad + len > BiggestDistance)
{
tree[par].cut = 1;
par = tree[par].father;
sum++;
}
else if (tree[temp].cut == 0 && tree[par].maxroad < tree[temp].maxroad + len)
{
tree[par].maxroad = tree[temp].maxroad + len;
}
if (--tree[par].out == 0)
push(par);
}
cout<<sum<<endl;
}
};