luogu P1272 重建道路

Posted ck666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P1272 重建道路相关的知识,希望对你有一定的参考价值。

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)的子树将被分离出来


 一开始想了各种神奇的东西,想着是不是要同时考虑上下两部分什么的,后来发现完全可以当成无根树嘛qwq

……↑以上请无视qwq

树形DP一道。没有什么奇奇怪怪的输入真是太好了

f[x][i]记录的是在以第x个点为根的子树上分出i个点的连通块最少需要删掉几条边

初始化f[x][1]当然等于这个点的度数和

dp转移方程f[x][j]=min(f[x][j],f[x][j-k]+f[v][k]-2)

要减2的原因是转移过来的两个连通块之间那条边被删了两次,然而合并后的连通块中这条边不应该被删

具体转移可以看代码qwq

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200;
int n,P,p[N],cnt,f[N][N],du[N];
bool vis[N];
struct edge{int to,nex;}e[N<<1];
void add(int u,int v)
{
    e[++cnt]=(edge){v,p[u]};
    p[u]=cnt;
}
void dfs(int x,int fa)
{
    f[x][1]=du[x];
    for(int i=p[x];i;i=e[i].nex)
    {
        int v=e[i].to;
        if(v==fa)continue;
        dfs(v,x);
        for(int j=P;j>=1;--j)
        for(int k=1;k<=j;++k)
        f[x][j]=min(f[x][j],f[x][j-k]+f[v][k]-2);
    }
}
int main()
{
    scanf("%d%d",&n,&P);
    memset(f,0x3f,sizeof(f));
    for(int i=1;i<n;++i)
    {
        int x,y;scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
        ++du[x],++du[y];
    }
    dfs(1,0);
    int ans=N;
    for(int i=1;i<=n;++i)ans=min(ans,f[i][P]);
    cout<<ans<<endl;
} 
road

by:wypx


 树上dp,随便乱搞就过了,真好玩

把我在luogu上的题解粘过来骗一波访问

把一颗树以1(假装)为根节点,计算出他有多少个儿子,把他加一就是有多少连出去的边。

重载dfs的w并没有什么用.....

维护一个连通块(dp[i][1]就是把i这个节点删到只剩它自己需要操作多少次),然后大力扩展连通块,通过father节点加点,使其保持p个点在块里。

然后枚举一遍在以没个i所扩展的大小为p的连通块所需要的操作次数

技术分享
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=200;
const int INT=2333333;
int read(){
    int an=0,f=1;
    char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
    while(0<=ch&&ch<=9){an=an*10+ch-0;ch=getchar();}
    return an*f;
}
int f[maxn],cnt,fa[maxn],ans=2333;
bool vis[maxn];
struct saber{
int to,nex;
}b[maxn<<1];
void add(int x,int y){
    cnt++;
    b[cnt].nex=f[x];
    b[cnt].to=y;
    f[x]=cnt;
}
int son[maxn],dp[maxn][maxn],m,n,p;
void dfs(int x){
    vis[x]=1;son[x]=1;
    for(int i=f[x];i;i=b[i].nex){
        int v=b[i].to;
        if(!vis[v]){
            dfs(v);
            fa[v]=x;
            son[x]++;
        }
    }
}
void dfs(int x,int w){
    dp[x][1]=son[x];
    for(int i=f[x];i;i=b[i].nex){
        int v=b[i].to;
         if(v!=fa[x]){
         dfs(v,w);
        for(int j=p;j>=1;j--)
            for(int k=1;k<=j;k++)
            dp[x][j]=min(dp[x][j],dp[x][j-k]+dp[v][k]-2);
        }
    }
    ans=min(ans,dp[x][p]);
}
int main(){
    n=read();p=read();
    for(int i=0;i<=maxn-9;i++)
    for(int j=0;j<=maxn-9;j++)dp[i][j]=INT;
    for(int i=1;i<n;i++){
        int x,y;
        x=read();y=read();
        add(x,y);
        add(y,x);
    }
    dfs(1);son[1]--;
    dfs(1,INT);
    cout<<ans;
    return 0;
} 
1272

by:s_a_b_e_r


 




以上是关于luogu P1272 重建道路的主要内容,如果未能解决你的问题,请参考以下文章

luogu P1272 重建道路

luogu P1272 重建道路 类似树上背包的树形dp

P1272 重建道路

P1272 重建道路(树形dp)

P1272 重建道路

[JZOJ5465]道路重建--边双缩点+树的直径