P1272 重建道路
P1272 重建道路
题目描述
- 一场可怕的地震后,人们用N个牲口棚(1≤N≤150,编号1..N)重建了农夫John的牧场。
由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是 惟一的。
因此,牧场运输系统可以被构建成一棵树。John想要知道另一次地震会造成多严重的破坏。
有些道路一旦被毁坏,就会使一棵含有P(1≤P≤N)个牲口棚的子树和剩余的牲口棚分离,John想知道这些道路的最小数目。
输入格式
- 第1行:2个整数,N和P
第2..N行:每行2个整数I和J,表示节点I是节点J的父节点。
输出格式
- 单独一行,包含一旦被破坏将分离出恰含P个节点的子树的道路的最小数目。
输入 #1
- 11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11
输出 #1
- 2
说明/提示
- 【样例解释】
如果道路1-4和1-5被破坏,含有节点(1,2,3,6,7,8)的子树将被分离出来
题意描述
-
有一棵树
-
删掉一些节点
-
使剩下的节点仍为一棵树,并恰好等于P;
-
求删的最小次数
主要思路:树形DP
-
首先输入整棵多叉树
-
然后将多叉树转为二叉树(方便计算)
-
求删掉该节点后,会有多少节点挂掉
-
最后将二叉树转成答案 -
遍历二叉树 DP+记忆化 求解
如何 DP ?
-
设 dp( i , j )为在节点i删j个节点的最小次数
-
对于节点i,有删和不删两种情况
-
如果删,则
dp( i , j ) = dp( t [ i ].r , j - t [ i ].d ) + 1
意为: 在右子树上删去(要删的 j 减去已经删的 t [ i ].d )节点的最小次数加上本次删的次数 1
-
如果删, 还有一种特殊情况
if(m==n-p) dp( i , j )=dp(t[ k ].l,t[ k ].d-p)+1;
意为: 若其他子树还未删任何节点,可以在删去的子树中删去(子树的节点减去要保留的节点),即等同于不删子树,而删去除该子树外的所有节点
-
如果不删,则
dp( i , j )=dp(t [ i ].l , k )+dp(t [ i ].r ,m-k )
意为: 节点i左子树删 k 个节点,右子树删 m-k 个节 点的最小次数和
-
加上记忆化
t [ i ].r为右子树,t [ i ].l为左子树
t [ i ].d为删掉该节点后,会有多少节点挂掉。
传朕旨意,宣代码
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;//流线型头文件
const int INF=9999999;
struct tree{ int d,l,r; }t[301];
int f[301][301];
int n,p;//定义树
int dp(int k,int m)
{
if(f[k][m]>0) return f[k][m];
if(m==0) return 0;//删0个节点次数为0
if(m<0) return INF;//不可能删负数个节点
if(k==0) return INF;//不存在的节点
int tmp;
f[k][m]=INF;
tmp=dp(t[k].r,m-t[k].d)+1;
if(tmp<f[k][m]) f[k][m]=tmp;
if(m==n-p) tmp=dp(t[k].l,t[k].d-p)+1;
if(tmp<f[k][m]) f[k][m]=tmp;
int lv,rv;
for(int i=0; i<=m; i++)
{
lv=dp(t[k].l,i);
if(lv>=INF) continue;
rv=dp(t[k].r,m-i);
if(rv>=INF) continue;
if(lv+rv<f[k][m]) f[k][m]=lv+rv;
}
return f[k][m];
}//树形DP+记忆化
void dfs(int x)
{
if(x==0) return;
if(t[x].l==0)
{
t[x].d=1;
return;
}
int r=t[x].l;
t[x].d+=1;
while(r>0)
{
dfs(r);
t[x].d+=t[r].d;
r=t[r].r;
}
}//遍历树,求删掉该节点后,会有多少节点挂掉
int main()
{
scanf("%d%d",&n,&p);
int x,y,r;
memset(t,0,sizeof(t));
memset(f,-1,sizeof(f));
for(int i=1; i<n; i++)
{
scanf("%d%d",&x,&y);
if(t[x].l==0) t[x].l=y;
else
{
r=t[x].l;
while(t[r].r>0) r=t[r].r;
t[r].r=y;
}//转二叉树
}
dfs(1);
cout<<dp(1,n-p)<<endl;
return 0;
}