您的位置:首页 > 产品设计 > UI/UE

POJ 1947 Rebuilding Roads 树形dp

2012-04-04 11:16 351 查看
http://poj.org/problem?id=1947

题意:给你一棵有N个结点的树, 要求从树中剪掉一些边, 使得最后得到的子树恰好有P个结点。

思路:树形dp。用dp[i][j] 表示在以i为根的子树中保留下j个结点最少需要剪掉的边数。这样在求i结点的不同j值时,就是对其孩子的一次背包。dp[i][j] = MIN(dp[i][j] , dp[son[i]][k]+dp[i][j-k]);

代码:

/*
树形dp
*/
#include<stdio.h>
#include<string.h>
#define MIN(a,b) (a)>(b)?(b):(a)
int N ,P ;
bool map[160][160] ;
int dp[160][160] ;
bool vis[160] ;
int root ,_min ;

void dfs(int u){

dp[u][1] = 0 ;
for(int j=2;j<=P;j++)
dp[u][j] = N + 1 ;
for(int v=1;v<=N;v++){
if(map[u][v] == 0)	continue ;
dfs(v) ;
for(int j=P;j>=1;j--){
dp[u][j]++ ;			//在孩子v中取0个结点,也就是说将孩子v连接的边去除,因此这里需要加1
for(int k=1;j-k>=1;k++){
dp[u][j] = MIN(dp[u][j] , dp[u][j-k]+dp[v][k] ) ;
}
}
}
}
int main(){
int a ,b ;
while(scanf("%d %d",&N,&P) == 2){
memset(map , 0 ,sizeof(map) );
memset(vis , 0 ,sizeof(vis) );
for(int i=1;i<N;i++){
scanf("%d %d",&a,&b);
map[a][b] = 1 ;
vis[b] = 1 ;
}
for(int i=1;i<=N;i++){
if(!vis[i]){
root = i ;
}
}
dfs(root) ;
_min = N + 1 ;
for(int i=1;i<=N;i++){
if(i == root){
_min = MIN(_min ,dp[i][P]);
}
else{
_min = MIN(_min , dp[i][P]+1);		//最后的最优子树不一定以原来的根为根
}
}
printf("%d\n",_min);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: