您的位置:首页 > 其它

POJ 1947 树形背包

2012-09-12 16:26 134 查看
题意:将一颗树分解成若干部分,其中一个部分还有的节点为p,求实现上述操作最少要删去几条边。

做法:树形背包。确立根节点,找出各自节点的子孙数目。做这题是先把每个子树确立为一个状态,并建立状态方程:dp[u][j]=min(dp[u][j-k]+dp[v][k]),其中,j k 分别代表u,v所要删去的节点,意思是求在以u为根的一棵树中,删掉j个节点所需要的删去的边数。这里dp[u][son[u]]=0,这个算式只在u有父节点的时候成立,他的父节点构成的树,要删掉son[u]个

节点,可以只需删去链接它和u 的那一条边。还有一个问题是最终生成答案的时候要遍历每一个节点,而且除总祖先节点外,为了独立形成子树,其他节点的dp值必须加1。
#include<stdio.h>
#include<string.h>
#define eps 1e8
#define LMT 155
/*类似的题,先要确立根节点*/
typedef struct
{
    int u,v,next;
}line;
line e[LMT<<1];
int dp[LMT][LMT],next[LMT],son[LMT];
int all,p,n;
inline int min(int a,int b)
{
    return a<b?a:b;
}
void insert(int u,int v)
{
    e[all].u=u;
    e[all].v=v;
    e[all].next=next[u];
    next[u]=all++;
    e[all].u=v;
    e[all].v=u;
    e[all].next=next[v];
    next[v]=all++;
}
void inidfs(int u,int pre)
{
    son[u]=1;
    int v,x;
    for(x=next[u];x!=-1;x=e[x].next)
    if(e[x].v!=pre)
    {
        v=e[x].v;
        inidfs(v,u);
        son[u]+=son[v];
    }
    dp[u][son[u]]=1;
    dp[u][0]=0;
}
void dfs(int u,int pre)
{
    int x,v,j,k;
    for(x=next[u];x!=-1;x=e[x].next)
    if(e[x].v!=pre)
    {
        v=e[x].v;
        dfs(v,u);
        for(j=son[u]-1;j>=0;j--)
          for(k=0;k<=son[v]&&k<=j;k++)
          if(dp[u][j-k]!=eps)
            dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]);
    }
}
int main()
{
    int i,ans,j;
    while(scanf("%d%d",&n,&p)!=EOF)
    {
        memset(next,-1,sizeof(next));
        memset(son,0,sizeof(son));
        all=0;
        for(i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            insert(u,v);
        }
        for(i=1;i<=n;i++)
         for(j=0;j<=n;j++)
         dp[i][j]=eps;
         inidfs(1,0);
         dfs(1,0);
         ans=dp[1][son[1]-p];
         for(i=2;i<=n;i++)
         if(son[i]>=p)
         ans=min(ans,dp[i][son[i]-p]+1);
         printf("%d\n",ans);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: