题目连接:http://poj.org/problem?id=1947
题意:
给你一颗树,问去掉一个n个节点的子树所需要删除的最少边数。
思路:可以简化处理:先求出对于每个点形成一个n个节点的树最少需要删除多少边,然后对于原树中的根,因为是它本身,所以不用+1,其他的因为还要切断与根的关系,要+1处理;
对于求以每个节点为根的树形成只有n个节点所需删除最少的边数的问题。用树状dp来做;
dp【i】【j】表示以i为根,拥有j个节点所需要删除的最少边。
方法一:
首先,认为每个节点只有其本身。那么初始化dp【i】【1】=0;
那么动态转移:
删除某个子节点的话:temp=dp【i】【j】+1;
不删除的话:dp【i】【j-k】+dp【子节点】【k】
所以:dp【i】【j】=min(temp,dp【i】【j-k】+dp【子节点】【k】);
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 0xfffffff
using namespace std;
int next[240][240];
int mark[240];
int bb;
int dp[240][240];
void dfs(int aa)
{
for(int ii=1;ii<=next[aa][0];ii++)
dfs(next[aa][ii]);
dp[aa][1]=0;
for(int ii=1;ii<=next[aa][0];ii++)
{
for(int kk=bb;kk>=1;kk--)
{
int temp=dp[aa][kk]+1;
for(int jj=1;jj<=kk;jj++)
{
temp=min(temp,dp[next[aa][ii]][jj]+dp[aa][kk-jj]);
//cout<<"aa = "<<aa<<' '<<"son = "<<next[aa][ii]<<' '<<"many = "<<jj+kk<<' '<<dp[aa][jj+kk]<<endl;
}
dp[aa][kk]=temp;
}
}
return ;
}
int DP(int aa,int nn)
{
dfs(aa);
int getmin=N;
for(int ii=1;ii<=nn;ii++)
{
if(ii==aa)continue;
getmin=min(getmin,dp[ii][bb]+1);
}
getmin=min(getmin,dp[aa][bb]);
return getmin;
}
void init(int nn)
{
for(int ii=1;ii<=nn;ii++)
{
for(int jj=0;jj<=nn;jj++)
dp[ii][jj]=N;
next[ii][0]=0;
}
return ;
}
int main()
{
int i,j,m,n;
int a,b;
while(scanf("%d%d",&m,&n)!=EOF)
{
bb=n;
memset(mark,0,sizeof(mark));
init(m);
for(i=1;i<m;i++)
{
scanf("%d%d",&a,&b);
next[a][++next[a][0]]=b;
mark[b]=1;
}
int temp;
for(i=1;i<=m;i++)
{
if(!mark[i])
{
temp=DP(i,m);
break;
}
}
printf("%d\n",temp);
}
return 0;
}
方法二:
首先,默认每个节点有其子节点,但是,都被删除掉了。那么初始化dp【i】【1】=(子节点的个数);
动态转移方程:
dp【i】【jj+kk】=min(dp【i】【jj】+dp【子节点】【kk】-1,dp【i】【jj+kk】);
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 0xfffffff
using namespace std;
int next[240][240];
int mark[240];
int bb;
int dp[240][240];
void dfs(int aa)
{
for(int ii=1;ii<=next[aa][0];ii++)
dfs(next[aa][ii]);
dp[aa][1]=next[aa][0];
for(int ii=1;ii<=next[aa][0];ii++)
{
for(int kk=bb-1;kk>=1;kk--)
{
if(dp[aa][kk]<N)
for(int jj=1;jj<=bb-1;jj++)
if(dp[next[aa][ii]][jj]<N)
{
dp[aa][jj+kk]=min(dp[aa][jj+kk],dp[next[aa][ii]][jj]+dp[aa][kk]-1);
//cout<<"aa = "<<aa<<' '<<"son = "<<next[aa][ii]<<' '<<"many = "<<jj+kk<<' '<<dp[aa][jj+kk]<<endl;
}
}
}
return ;
}
int DP(int aa,int nn)
{
dfs(aa);
int getmin=N;
for(int ii=1;ii<=nn;ii++)
{
if(ii==aa)continue;
getmin=min(getmin,dp[ii][bb]+1);
}
getmin=min(getmin,dp[aa][bb]);
return getmin;
}
void init(int nn)
{
for(int ii=1;ii<=nn;ii++)
{
for(int jj=0;jj<=nn;jj++)
dp[ii][jj]=N;
next[ii][0]=0;
}
return ;
}
int main()
{
int i,j,m,n;
int a,b;
while(scanf("%d%d",&m,&n)!=EOF)
{
bb=n;
memset(mark,0,sizeof(mark));
init(m);
for(i=1;i<m;i++)
{
scanf("%d%d",&a,&b);
next[a][++next[a][0]]=b;
mark[b]=1;
}
int temp;
for(i=1;i<=m;i++)
{
if(!mark[i])
{
temp=DP(i,m);
break;
}
}//题目没有说一定是1为所给树的祖先吧
printf("%d\n",temp);
}
return 0;
}