Luogu P3698 [CQOI2017]小Q的棋盘 (树形DP)
https://www.luogu.com.cn/problem/P3698
- f[u][j] 表示在以u为根节点的子树中分配多少步能走到的最多的点数,树上背包那样转移,但这样显然是不对的,跑到另一棵子树时候还要往回走而从消耗步数
- 影响每棵树的可分配步数以及遍历子树情况的是是否回到根节点
- f[u][j][1/0] 表示在以u为根节点的子树中分配多少步是/否回到根节点能走到的最多的点数
u不回到根节点,v可回可不回。
v不回:因为v一定是从u过来的所以这种情况一定是从u回根转移过来的。(如果v是子树中第一个转移的子节点,则1/0没有影响)
v回:它可以从f[u][j - t][0]也可以从f[u][j - t][1]转移过来,为什么可以从f[u][j - t][0]转移过来?不是要先从之前节点出来回到根节点吗? 其实因为背包是没有顺序的,只要看作先走的回根节点的v,不回的是从回的转移过来,这样不回的答案也不会相互叠加。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define PII pair<ll, ll>
const int N = 1e2 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double PI = acos(-1.0);
int h[N], e[N << 1], ne[N << 1], idx;
int n, m;
int f[N][N][2];
void add ( int a, int b ) {
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dfs( int u, int fa ) {
f[u][0][0] = f[u][0][1] = 1;
for ( int i = h[u]; ~i; i = ne[i] ) {
int v = e[i]; if( v == fa ) continue;
dfs(v, u);
for ( int j = m; j >= 1; -- j ) {
for ( int t = 1; t <= j; ++ t ) {
f[u][j][0] = max(f[u][j][0], f[u][j - t][1] + f[v][t - 1][0]);
if(t >= 2) {
f[u][j][0] = max(f[u][j][0], f[u][j - t][0] + f[v][t - 2][1]);
f[u][j][1] = max(f[u][j][1], f[u][j - t][1] + f[v][t - 2][1]);
}
}
}
}
}
int main () {
IOS
memset(h, -1, sizeof h);
cin >> n >> m;
for ( int i = 1; i <= n - 1; ++ i ) {
int u, v; cin >> u >> v; ++ u, ++ v;
add(u, v); add(v, u);
}
dfs(1, -1);
int ans = 0;
for (int i = 0; i <= m; i++)
ans = max(ans, f[1][i][0]);
cout << ans << endl;
return 0;
}