zoukankan      html  css  js  c++  java
  • P1272 重建道路(树形dp)

    P1272 重建道路

    题目描述

    一场可怕的地震后,人们用N个牲口棚(1≤N≤150,编号1..N)重建了农夫John的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的。因此,牧场运输系统可以被构建成一棵树。John想要知道另一次地震会造成多严重的破坏。有些道路一旦被毁坏,就会使一棵含有P(1≤P≤N)个牲口棚的子树和剩余的牲口棚分离,John想知道这些道路的最小数目。

    输入输出格式

    输入格式:

    第1行:2个整数,N和P

    第2..N行:每行2个整数I和J,表示节点I是节点J的父节点。

    输出格式:

    单独一行,包含一旦被破坏将分离出恰含P个节点的子树的道路的最小数目。

    输入输出样例

    输入样例#1:
    11 6
    1 2
    1 3
    1 4
    1 5
    2 6
    2 7
    2 8
    4 9
    4 10
    4 11
    
    输出样例#1:
    2
    

    说明

    【样例解释】

    如果道路1-4和1-5被破坏,含有节点(1,2,3,6,7,8)的子树将被分离出来 

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 151
    
    using namespace std;
    int n,m,ans,cnt,flag,w;
    int head[N],son[N],vis[N];
    struct node
    {
        int u,v,next; 
    }e[N<<1];
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    inline void add(int u,int v)
    {
        e[++cnt].v=v;e[cnt].next=head[u];head[u]=cnt;
    }
    
    void dfs2(int u,int tot)
    {
        if(son[u]==m)
        {
            w=tot;
            return;
        }
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].v;if(vis[i])continue;
            if(son[u]-son[v]>=m)
            {
                vis[i]=1;
                son[u]-=son[v];
                tot=tot+1;
                dfs2(u+1-1,tot);
                vis[i]=0;
            }
        }
    }
    
    void dfs(int u)
    {
        if(flag) return;
        for(int i=head[u];i;i=e[i].next)
        {
            dfs(e[i].v);
            if(son[u]<m) continue;
            else if(son[u]==m) 
            {
                printf("1
    ");
                flag=1;return;
            }
            else 
            {
                int tmp=son[u];
                dfs2(u,0);
                ans=min(ans,w);
                son[u]=tmp;
            }
        }
        if(flag) return;
    }
    
    void dfs1(int u)
    {
        son[u]=1;
        for(int i=head[u];i;i=e[i].next)
        {
            dfs1(e[i].v);
            son[u]+=son[e[i].v];
        }
    }
    
    int main()
    {
        int x,y;
        n=read();m=read();
        if(m==1)
        {
            printf("1
    ");
            return 0;
        }
        for(int i=1;i<n;i++)
        {
            x=read();y=read();
            add(x,y);
        }
        ans=0x3f3f3f3f;dfs1(1);
        dfs(1);
        if(!flag) printf("%d
    ",ans);
        return 0;
    }
    42分错误暴力
    /*
    显然树形dp
    dp[i][j]:i为根断掉子树大小为j最小边数
    初始化dp[u][1]=1的度数
    转移时枚举当前点断掉多少,算出连到的儿子断掉多少
    因为由儿子转移过来,他们之间的连边不能断
    但是转移时断掉了两次,所以答案减2 
    */
    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 151
    #define inf 0x7f7f7f7f
    
    using namespace std;
    int dp[N][N],head[N],d[N];
    int n,m,ans,cnt;
    struct node
    {
        int u,v,next; 
    }e[N<<1];
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    inline void add(int u,int v)
    {
        e[++cnt].v=v;e[cnt].next=head[u];head[u]=cnt;
    }
    
    void dfs(int u,int fa)
    {
        dp[u][1]=d[u];
        for(int i=head[u];i;i=e[i].next)
        {
            if(e[i].v!=fa)
            {
                dfs(e[i].v,u);
                for(int j=m;j>=1;j--)
                  for(int k=1;k<=j;k++)
                    dp[u][j]=min(dp[u][j],dp[e[i].v][k]+dp[u][j-k]-2);
            }
        }ans=min(ans,dp[u][m]);
    }
    
    int main()
    {
        int x,y;
        memset(dp,1,sizeof dp);
        n=read();m=read();
        for(int i=1;i<n;i++)
        {
            x=read();y=read();
            add(x,y);add(y,x);
            d[x]++;d[y]++;
        }
        ans=inf;
        dfs(1,0);
        printf("%d
    ",ans);
        return 0;
    }
    折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
  • 相关阅读:
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Navicat查询哪些表有指定字段名
  • 原文地址:https://www.cnblogs.com/L-Memory/p/7470215.html
Copyright © 2011-2022 走看看