另类做法求调(或证明为假
查看原帖
另类做法求调(或证明为假
844951
yyyx_o歪叉o楼主2024/7/26 07:44

昨天和同学叠做题数,做到这题挂了...

同学做法是以 00 为虚根,使 fi,jf_{i,j} 表示在 ii 节点,取 jj 个点的最大值。

而我是跑以父亲为 00 的点为根的若干棵子树,求出 gi,jg_{i,j} 表示第 ii 棵树上选 jj 个点的最大值,最后 0101 背包求全局最大值。

另外,如果只把主函数改成

......
dfs(0);
printf("%d",f[0][m+1]);

这道题就过了(即同学和大部分题解做法

#include <bits/stdc++.h>
using namespace std;

template <typename T>
inline void read(T &x)
{
    x = 0;
    register char c = getchar();
    register short f = 1;
    while (c < '0' || c > '9')
    {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    x *= f;
}

template <typename T, typename... Args>
inline void read(T &x, Args &...temps)
{
    read(x), read(temps...);
}

const int N = 305;
int n, m, s[N];
vector<int> G[N];
bool deg[N];
int f[N][N], cnt;
int siz[N];
int g[N][N];
int dp[N];

void dfs(int x)
{
    f[x][1] = s[x];
    siz[x] = 1;
    for (auto y : G[x])
    {
        dfs(y);
        siz[x] += siz[y];
        for (int i = siz[x]; i; i--)
            for (int j = i - 1; j; j--)
                f[x][i] = max(f[x][i], f[x][i - j] + f[y][j]);
    }
}

signed main()
{
    read(n, m);
    for (int i = 1, x; i <= n; i++)
        read(x, s[i]),
            deg[i] |= !!x,
            G[x].emplace_back(i);
    for (int i = 1; i <= n; i++)
        if (!deg[i])
        {
            cnt++, dfs(i);
            for (int j = 1; j <= m;j++)
                g[cnt][j] = f[i][j];
        }
    if (!cnt)
    {
        if (n == m)
        {
            int ans = 0;
            for (int i = 1; i <= n; i++)
                ans += s[i];
            printf("%d", ans);
        }
        else
            puts("0");
    }
    else
    {
        for (int i = 1; i <= cnt; i++)
            for (int j = 0; j <= m; j++)
                for (int k = m; k >= j; k--)
                    dp[k] = max(dp[k], dp[k - j] + g[i][j]);
        printf("%d", dp[m]);
    }

    return 0;
}
2024/7/26 07:44
加载中...