poj 1947

来源:互联网 发布:风驰网络加速器官网 编辑:程序博客网 时间:2024/05/01 15:38

题目连接: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 0xfffffffusing 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 0xfffffffusing 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;}


原创粉丝点击