• POJ1947


    题目大意

    给定一棵n个结点的树,问最少需要删除多少条边使得某棵子树的结点个数为p

    题解

    很经典的树形DP~~~直接上方程吧 dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]-1) 方程的意思是 以u结点为根保留j个结点需要删除的最少的边的条数,那么可以选择在某个子结点v中选择k个保留,其他结点保留j-k个,为什么需要-1呢,因为相当于把子树v衔接到结点u上,因此边u->v是不需要删除的,所以要-1

    代码:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    #define MAXN 155
    #define INF 0x3f3f3f3f
    struct node
    {
        int v,next;
    };
    node edge[MAXN];
    int head[MAXN],dp[MAXN][MAXN],p;
    void add_edge(int u,int v,int &k)
    {
        edge[k].v=v;
        edge[k].next=head[u];
        head[u]=k++;
    }
    void dfs(int u)
    {
        if(head[u]==-1)
        {
            dp[u][1]=0;
            return;
        }
        int cnt=0;
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].v;
            dfs(v);
            cnt++;
        }
        dp[u][1]=cnt;
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].v;
            for(int j=p; j>=2; j--)
                for(int k=1; k<=j; k++)
                    dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]-1);
        }
    }
    int main()
    {
        int n;
        while(scanf("%d%d",&n,&p)!=EOF)
        {
            int k=1;
            memset(head,-1,sizeof(head));
            memset(dp,INF,sizeof(dp));
            for(int i=1; i<n; i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                add_edge(u,v,k);
            }
            dfs(1);
            int ans=dp[1][p];
            for(int i=2; i<=n; i++)
                ans=min(ans,dp[i][p]+1);
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    数论——欧拉函数
    数论——最大公约数
    Python——循环
    数论——素数和反素数
    数论——快速幂剖析
    Perfect Keyboard
    HTML学习笔记Day6
    HTML学习笔记Day5
    HTML学习笔记Day4
    HTML学习笔记Day3
  • 原文地址:https://www.cnblogs.com/zjbztianya/p/3428481.html
Copyright © 2020-2023  润新知