贪心 A-兔子与樱花

兔子与樱花

兔子与樱花原题
在这里插入图片描述

题解

思路

需要求出最大的可以删除的节点的数量。

我们从叶子节点开始删。为什么不从别的节点开始呢?假设我们不从叶子节点开始,那么每一次的删除都需要考虑节点的父节点的权重与该节点的权重和子节点的数量,而如果我们从叶子节点开始,只需要考虑节点的权重与其父节点的权重即可。相对而言比较简单。

要删除的叶子节点最多,就需要删除的节点的权重尽可能小。这样,集中到其父节点的权重也会尽可能地小,可删除的节点就会增多。

实现

首先,要去找叶子节点,可以用深度优先搜索。
之后,每次都需要删除当前最小的权重,可以用排序或者优先队列。

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<string>
#include<queue>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<deque>
#include<vector>
#include<ctime>

using namespace std;
//#pragma GCC optimize(2)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define ull unsigned long long
#define ll long long
#define rep(i, x, y) for(int i=x;i<=y;i++)
#define mms(x, n) memset(x, n, sizeof(x))
#define mmc(a, b) memcpy(a, b, sizeof(b))
#define INF (0x3f3f3f3f)
#define mod (ull)(1e9+7)
typedef pair<int, int> P;

namespace FastIO {
    const int bufsiz = 1 << 22;
    char buf[bufsiz];

    inline char getch() {
        static int tot;
        tot++;
        if (tot == bufsiz) {
            fread(buf, 1, bufsiz, stdin);
            tot = 0;
        }
        return buf[tot];
    }

    inline int read() {
        int x = 0;
        char c = getch();
        while (!isdigit(c))c = getch();
        while (isdigit(c))x = x * 10 + c - '0', c = getch();
        return x;
    }
}
using FastIO::read;
// 洛谷的该题不用快读会TLE。
const int N = 2e6 + 10;
int c[N];
vector<int> son[N];
int ans;
int n, m;

bool cmp(int a, int b) {
    return c[a] < c[b];
}

void dfs(int node) {
    if (son[node].empty()) return;
    int size = son[node].size();
    for (int i = 0; i < size; i++) {
//        int nNode = ;
        dfs(son[node][i]);
    }
    sort(son[node].begin(), son[node].end(), cmp);
    for (int i = 0; i < size; i++) {
        int x = c[node] - 1 + c[son[node][i]];
        if (x <= m) {
            ans++;
            c[node] = x;
            // 这里有个Tips。
            // 存在情况:该节点的子节点有子节点。
            // 开始我做的时候把每个子节点的子节点都挪到了当前节点,并且在循环中对每个新增的节点都判断是否能够移动到当前节点。
            // 其实是完全不必要的。在我们删除了子节点之后,其权重会增加到当前节点,而既然在该子节点作为父节点时,并没有删除其子节点,说明删除后该子节点的权重加上其子节点的数量会超载。在删除了该子节点后,父节点拥有了其权重,父节点的权重加上被删节点的子节点的数量大于等于被删节点的权重加上子节点的数量,必然超载,所以不能对子节点进行删除。
        } else break;
    }
}

int main() {
    n = read(), m = read();
//    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) {
//        scanf("%d", &c[i]);
        c[i] = read();
    }
    for (int i = 0; i < n; i++) {
        int size;
//        scanf("%d", &size);
        size = read();
        c[i] += size;
        while (size--) {
            int tmp = read();
//            scanf("%d", &tmp);
            son[i].push_back(tmp);
        }
    }
    dfs(0);
    printf("%d", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45934120/article/details/107638557