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;
}
折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
原文地址:https://www.cnblogs.com/L-Memory/p/7470215.html