贪心算法——d森林——含测试用例

贪心算法

问题描述

编程实现T/S的d森林问题。

设T为一带权树,树中的每个边的权都为整数。又设S为T的一个顶点的子集,从T中删除S中的所有结点,则得到一个森林,记为T/S。如果T/S中所有树从根到叶子节点的路径长度都不超过d,则称T/S是一个d森林。设计一个算法求T的最小顶点集合S,使T/S为一个d森林。

问题分析

使用贪心的思想,删除最少的节点,即S中元素个数最少。

原理分析

使用算法

使用贪心的思想,从叶子节点向上遍历,每个节点的priceOfson记录该节点子节点中路径长度最大值,若priceOfson大于d则删除该节点,每一次遍历访问 无子节点或子节点均被访问或删除 且 未被标记 的节点,直到所有节点均被访问,得到最优解。

性质证明

设a为一节点,b1, b2 …… bi 为他的子节点,从叶子节点到bi的路径长度设为Li,当Li>d的字节点个数大于2时,删除a为更优解,当Li>d的个数为1时,删除子节点和a的个数相同,当Li > d的节点个数为0时,不删除节点。

综上所述,删除高度更大的节点更符合贪心性质,可以得到更优解。

算法实现

#include <iostream>
#include <stack>
#include <vector>
#include <math.h>
using namespace std;
struct node
{
    int id;
    vector<int> son;
    int parent;
    int priceOfson;
    int priceOfme;
    int markedson;
};

bool cmp(int a, int b) { return a>b; }

int main()
{
    int d, n, i = 0, j = 0;
    cin>>n>>d;
    node xx;
    vector<node> a(n+1, xx);
    vector<node> root;
    vector<int> marked(n+1, 0);
    xx.id = 1;
    root.push_back(xx);
    for(i = 1; i <=n; i++)
    {
        int x;
        cin>>x;
        a[i].id = i;
        for(j = 0; j < x; j++)
        {
            int y = 0, z = 0;
            cin>>y>>z;
            a[i].son.push_back(y+1);
            a[y+1].parent = i;
            a[y+1].priceOfme = z;
        }
    }
    cout<<"**"<<endl;
    vector<int> S;
    vector<int> cut(n+1, 0);
    int ee = 0;
    int cc = 0;
    while(ee < n)
    {
        for(i = 1; i <= n; i++)
        {
            if(cut[i]) continue; 
            if(!marked[i] && !cut[i] && a[i].markedson == a[i].son.size())
                // 该节点未被访问 且 未被删除 且 不是根结点 且 儿子节点已经全部被访问(即无子)  
            {   
                ee++;
                // the price of me and max price of my son
                if(a[i].priceOfson > d)
                {
                    S.push_back(a[i].id);
                    cut[i] = 1;
                    marked[i] = 1;
                    a[a[i].parent].markedson++;
                }
                else
                {
                    cc++;
                    // update parents' priceofson
                    if(a[a[i].parent].priceOfson < a[i].priceOfme + a[i].priceOfson)
                        a[a[i].parent].priceOfson = a[i].priceOfme + a[i].priceOfson; 
                    a[a[i].parent].markedson++;
                    marked[i] = 1;
                }
            }
        }
    }
    cout<<S[0]<<" "<<S.size()<<endl;
    return 0;
}

测试用例在发布的资源中——不会添加文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值