几个树形背包

👴大概是会简单的树形dp了

①P1272 重建道路

给你一棵树,要你去切除一些边,每次操作可以切掉一条边,找到能切出大小为p的子树最小的操作次数。

以dp[x][from]表示在from这个点上,其子树大小为x(包括自己)的最小切割次数。

如果要切出一颗子树,一定要在这个点与父节点之间切一刀,但root没有父节点,所以需要特判。

转移方程为:\(dp[i + j][from] = min(dp[i + j][from], dp[i][from] + dp[j][to] - 2)\);(-2是因为from和to之间的这条边被切割了两次,要补回去)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pll pair<ll,ll>
#define pii pair<int,int>
#define vii vector<int,int>
#define vll vector<ll>
#define vpii vector<pii>
#define vpll vector<pll>
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
double pi = acos(-1);
const double eps = 1e-6;
const int inf = 1e9 + 7;
const ll lnf = 1e18 + 7;
const int maxn = 2e3 + 10;
ll mod = 998244353;

int n, p;

int dp[155][155];

vector<int>G[155];

int siz[155];

void dfs(int from)
{
    siz[from] = 1;
    dp[1][from] = G[from].size() + 1;
    for (auto to : G[from])
    {
        dfs(to);
        for (int i = siz[from]; i >= 1; i--)
            for (int j = siz[to]; j >= 1; j--)
                dp[i + j][from] = min(dp[i + j][from], dp[i][from] + dp[j][to] - 2);
        siz[from] += siz[to];
    }
}

int main()
{
    fastio;
    int n, p;
    cin >> n >> p;
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
            dp[i][j] = inf;
    for (int i = 1; i < n; i++)
    {
        int x, y;
        cin >> x >> y;
        G[x].push_back(y);
    }
    dfs(1);
    /*int ans = n;
    for (int i = 1; i <= n; i++)
        ans = min(min(dp[p][i], dp[n - p][i]), ans);*/
    int ans = dp[p][1] - 1;
    for (int i = 2; i <= n; i++)
        ans = min(ans, dp[p][i]);
    cout << ans << endl;

    return 0;

}

②P1273 有线电视网

也是一棵树,叶子节点有正权,每条边有负权,选叶子节点的前置条件是其到root的路径上的所有点都被激活(即到这个叶子节点所有的负权边都要被选,每条边只计一次权值)。

以dp[x][from]表示选择x个叶子节点,from这个点可以获得的最大权值。

到达叶子节点时:

    siz[from] = 1;
    dp[1][from] = w[from];
    dp[0][from] = 0;
    return;

转移方程:\(dp[i + j][from] = max(dp[i + j][from], dp[i][from] + dp[j][to] - cost)\);cost为边权

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pll pair<ll,ll>
#define pii pair<int,int>
#define vii vector<int,int>
#define vll vector<ll>
#define vpii vector<pii>
#define vpll vector<pll>
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
double pi = acos(-1);
const double eps = 1e-6;
const int inf = 1e9 + 7;
const ll lnf = 1e18 + 7;
const int maxn = 3e3 + 10;
ll mod = 998244353;

int n, p;

int dp[maxn][maxn], w[maxn];

vector<pii>G[maxn];

int siz[maxn];

void dfs(int from)
{
    if (G[from].empty())
    {
        siz[from] = 1;
        dp[1][from] = w[from];
        dp[0][from] = 0;
        return;
    }
    for (auto now : G[from])
    {
        int to = now.first, cost = now.second;
        dfs(to);
        for (int i = siz[from]; i >= 0; i--)
            for (int j = siz[to]; j >= 0; j--)
                dp[i + j][from] = max(dp[i + j][from], dp[i][from] + dp[j][to] - cost);
        siz[from] += siz[to];
    }
}

int main()
{
    fastio;
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            dp[i][j] = -inf;
    for (int i = 1; i <= n - m; i++)
    {
        int k;
        cin >> k;
        while (k--)
        {
            int x, y;
            cin >> x >> y;
            G[i].push_back({ x,y });
        }
    }
    for (int i = n - m + 1; i <= n; i++)
        cin >> w[i];
    dfs(1);
    int ans = 0;
    for (int i = m; i >= 0; i--)
    {
        if (dp[i][1] >= 0)
        {
            cout << i;
            break;
        }
    }

    return 0;

}

③ICPC2020南京 M.Monster Hunter

题意(:给你一颗完整的树,你可以从中消除某些节点,使其分裂成很多个子树。对于每一个单独的子树计算权值,除了它的root的权值只计1次,其他每个点的权值要计2次.问进行x次操作,分裂的的所有子树统计后得到的最小权值。

dp[0/1][x][from]表示在点from的子树中,使用x次操作时,选(不选)from的最小花费。

状态转移方程:

if (j + i < siz[from] + siz[to])
    tmp[0][j + i] = min(tmp[0][j + i], dp[0][j][from] + min(dp[1][i][to], dp[0][i][to] + w[to]));//如果不使用操作,则需要在子节点是否使用操作的状态中取min
if (j > 0)
    tmp[1][j + i] = min(tmp[1][j + i], dp[1][j][from] + min(dp[1][i][to], dp[0][i][to]));//如果使用操作,则子节点不使用操作也不需要加上子节点的

在叶子节点时初始化:

dp[0][0][from] = w[from];
dp[1][1][from] = 0;
dp[1][0][from] = dp[0][1][from] = lnf;
return;
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pll pair<ll,ll>
#define pii pair<int,int>
#define vii vector<int,int>
#define vll vector<ll>
#define vpii vector<pii>
#define vpll vector<pll>
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;
double pi = acos(-1);
const double eps = 1e-6;
const int inf = 1e9 + 7;
const ll lnf = 1e18 + 7;
const int maxn = 2e3 + 10;
ll mod = 998244353;

vector<int>G[maxn];

ll dp[2][maxn][maxn];
int siz[maxn];
ll tmp[2][maxn], w[maxn];

void init(int n)
{
    for (int i = 0; i <= n; i++)
    {
        siz[i] = 0;
        G[i].clear();
        for (int j = 0; j <= n; j++)
            dp[0][i][j] = dp[1][i][j] = 0;
    }
}

void dfs(int from)
{
    siz[from] = 1;
    if (G[from].empty())
    {
        dp[0][0][from] = w[from];
        dp[1][1][from] = 0;
        dp[1][0][from] = dp[0][1][from] = lnf;
        return;
    }
    for (auto to : G[from])
    {
        dfs(to);
        for (int i = 0; i <= siz[from] + siz[to]; i++)tmp[0][i] = tmp[1][i] = lnf;
        for (int i = 0; i <= siz[to]; i++)
            for (int j = 0; j <= siz[from]; j++)
            {
                if (j + i < siz[from] + siz[to])
                    tmp[0][j + i] = min(tmp[0][j + i], dp[0][j][from] + min(dp[1][i][to], dp[0][i][to] + w[to]));
                if (j > 0)
                    tmp[1][j + i] = min(tmp[1][j + i], dp[1][j][from] + min(dp[1][i][to], dp[0][i][to]));
            }
        siz[from] += siz[to];
        for (int i = 0; i <= siz[from]; i++)
            dp[0][i][from] = tmp[0][i], dp[1][i][from] = tmp[1][i];
    }
    for (int i = 0; i <= siz[from]; i++)
        dp[0][i][from] += w[from];
}

int main()
{
    fastio;
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        init(n);
        for (int i = 2; i <= n; i++)
        {
            int x;
            cin >> x;
            G[x].push_back(i);
        }
        for (int i = 1; i <= n; i++)cin >> w[i];
        dfs(1);
        for (int i = 0; i <= n; i++)
        {
            cout << min(dp[0][i][1], dp[1][i][1]) << " ";
        }
        cout << endl;
    }

    return 0;

}
posted @ 2021-03-31 21:24  Lecoww  阅读(53)  评论(0编辑  收藏  举报