時間限制: 1 sec 記憶體限制: 128 mb
提交: 11 解決: 7 [
提交][
狀態][
討論版]
現在有一棵n個結點的樹(結點從1到n編號),請問至少要刪除幾條邊,才能得到乙個恰好有p個結點的子樹?
第一行輸入兩個數n和p (1 <= n<= 150, 1 <= p<= n)
接下來輸入n-1行,每行兩個整數x y,表示x和y之間有一條邊。
輸出答案。
11 61 21 3
1 41 5
2 62 7
2 84 9
4 10
4 11
2如果1-4 和 1-5 兩條邊刪除,結點1, 2, 3, 6,
7, 8會形成一顆有6個結點的子樹。
分析:
之前方法不對的乙個題。樹形dp。
數型dp總結一下。
用 dp[i][j] 表示以i節點為根,截出含有j個點的連通子樹所需要截的最少次數。
那麼可以得到初始化 dp[i][1]=du[i] (du[i]為i的入邊與出邊的總和),意思是只選i這乙個節點,那麼當然要把與它相連的邊都截掉。
那麼狀態轉移方程怎麼得到呢
以樣例為例子,節點1連線 2,3,4,5 。節點2連線 6,7,8。遞迴著進行動規之後我們可以得到 dp[2][3]=2 (擷取1-2和2-8)
那麼dp[1][4]=min(dp[1][4],dp[2][3]+dp[1][1]-2)
為啥要減2
因為dp[1][1]是刪了一次 1-2 的結果,dp[2][3]也刪了一次 1-2,但事實上得到dp[1][4]時 1-2是連通的,所以把這刪的兩次補上。
具體動規按照分組揹包的迴圈順序跑。
狀態轉移方程:
dp[u][j]=min(dp[u][j],dp[u][k]+dp[v][j-k]-2);
從父親節點選k個,從兒子節點擊j-k個。
1 #include2 #include3 #include4 #include5 #include6#define ll long long
7#define m(a) memset(a,0,sizeof a)
8#define fo(i,j,k) for(i=j;i<=k;i++)
9using
namespace
std;
10const
int mxn=155
;11 vector f[mxn];
12int
n,p;
13int
dp[mxn][mxn],du[mxn];
14 inline void dfs(int
u)1527}
28int
main()
2940 dfs(1
);41 fo(i,1
,n)42 ans=min(ans,dp[i][p]);
43 printf("
%d\n
",ans);
44return0;
45 }
基礎演算法 重建道路
題目 描述 輸入 輸出 樣例輸入 樣例輸出 分析 一場可怕的 後,人們用n個牲口棚 1 n 150,編號1.n 重建了farmer john的牧場。由於人們沒有時間建設多餘的道路,所以現在從乙個牲口棚到另乙個牲口棚的道路是惟一的。因此,牧場運輸系統可以被構建成一棵樹。john想要知道另一次 會造成多...
重建道路 樹上揹包
初始化 void init struct edges edge maxm 1 無向圖則需要乘2 inline void add int u,int v head u cnt int dp m m siz m tmp m int n,m void dfs int u,int fa siz u siz ...
P3905 道路重建
p3905 道路重建 我一開始想錯了,我的是類似kruskal,把毀壞的邊從小到大加,並且判斷聯通性。但是這有乙個問題,你可能會多加,就是這條邊沒用,但是它比較小,你也加上了。居然還有10分,資料也是水水的。include include include include include includ...