P1272 重建道路 | 樹上揹包
阿新 • • 發佈:2021-09-16
P1272 重建道路 | 樹上揹包
題目描述
從一棵樹上選擇數量最少的邊斷開 使得拆出的子樹大小為P
題目描述
從一棵樹上選擇數量最少的邊斷開 使得拆出的子樹大小為P
題解
樹上dp
設 \(f[i][j]\) 表示子樹i中拆分出j個節點 需要最少刪去多少邊
考慮樹上揹包的模板 我們對於一個節點now
合併時候 把該節點的每個子樹看成一個組 子樹中每一個點看成物品進行分組揹包
由於每個非根節點想要單獨拆分出來,所以答案要+1
最後,由於拆分並不需要在根節點進行
所以對於每個節點都可以看一看有沒有更優秀的答案
code:
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f using namespace std; #define maxn 159 int n,p; int f[maxn][maxn]; vector<int> son[maxn]; int dfs(int now,int fa)//返回子樹個數 { f[now][1]=0; int size=1; for(int i=0;i<son[now].size();i++) { int to=son[now][i]; if(to==fa)continue; int tk=dfs(to,now); size+=tk; for(int j=size;j>=0;j--)//分j組 { f[now][j]++; for(int k=0;k<=min(tk,j-1);k++)//j-1強制選根 { f[now][j]=min(f[now][j],f[to][k]+f[now][j-k]); } } } return size; } signed main() { scanf("%d%d",&n,&p); memset(f,0x3f,sizeof(f)); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); son[x].push_back(y); son[y].push_back(x); } dfs(1,0); int ans=f[1][p]; for(int i=2;i<=n;i++)ans=min(ans,f[i][p]+1); printf("%d\n",ans); return 0; } /* f[i][j]表示子樹i中分離出j個節點需要多少道路 強制當前子樹i和父節點相連 f[i][j]變成了 有s個物品 每個節點有 */ ``