??大概是会简单的树形dp了
给你一棵树,要你去切除一些边,每次操作可以切掉一条边,找到能切出大小为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;
}
也是一棵树,叶子节点有正权,每条边有负权,选叶子节点的前置条件是其到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;
}
题意(:给你一颗完整的树,你可以从中消除某些节点,使其分裂成很多个子树。对于每一个单独的子树计算权值,除了它的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;
}
原文:https://www.cnblogs.com/ruanbaiQAQ/p/14603850.html