重建道路

题目:

Description

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

Input

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

Output

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


树形DP 用了两种方式)

一:边集数组

  1. f[x][p]表示以x为根的子树保留p个点要删除的最小边数
  2. 状态转移方程:f[x][j]=min(f[x][j],f[t[i].v][k]+f[x][j-k]-2)
  3. 转移时枚举当前点删除多少边,儿子节点能删除多少条边
  4. 因为由儿子转移过来,他们之间的连边不能断,but转移时断掉了两次,所以ans-2

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int N=305,M=100000000;
int n,p,a[N],d[N],f[N][N],tot,ans;
 struct ss
{
    int nx,v;
}t[N*2];
 void add(int u,int v)//边集数组
{
    tot++; t[tot].v=v; t[tot].nx=a[u]; a[u]=tot;
}
 void dp(int x,int fa)//x为当前节点,fa为当前节点的父节点
{
    f[x][1]=d[x]; //x节点保留1个节点时将与x节点相连的子节点断开 
    int i,j,k;
    for (k=a[x]; k; k=t[k].nx)  
        if (t[k].v!=fa)
        {
            dp(t[k].v,x);
            for (i=p; i>=1; i--)
                for (j=1; j<=i; j++) f[x][i]=min(f[x][i],f[t[k].v][j]+f[x][i-j]-2);
        }
    ans=min(ans,f[x][p]);
}
 int main()
{
    memset(f,0x03f,sizeof(f));
    scanf("%d%d",&n,&p);
    int x,y;
    for (int i=1; i<n; i++)
    {
        scanf("%d%d",&x,&y);
        d[x]++; d[y]++;
        add(x,y); add(y,x);
    }
    ans=M;
    dp(1,0);
    printf("%d\n",ans);
    return 0;
}

二:f[x][p]表示以x为根的子树保留p个点要删除的最小边数

  • res[rx][p]表示x节点右边最近的节点保留p个点要删除的最小边数
  • 上一种方式相比,多用res数组记录兄弟节点的值,而上一种则是从p至1循环防止前面更新的值对后面的值产生影响
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int N=305,M=1000000000;
int a[N],nx[N],f[N][N],res[N][N];
int n,p,root,ans=M;
bool flag[N];
 void dfs(int x)
{
    for (int i=0; i<=p; i++) f[x][i]=res[x][i]=M;
    f[x][0]=1;
    if (a[x]==0)
    {
        f[x][1]=0;
        return;
    }
    int i,j,k,rk=0;
    for (k=a[x]; k; k=nx[k])
    {
        dfs(k);
        if (rk>0)
            for (i=0; i<=p; i++)
                for (j=0; j<=i; j++) 
                    res[k][i]=min(res[k][i],res[rk][j]+f[k][i-j]);
        else for (i=0; i<=p; i++) res[k][i]=f[k][i];
        rk=k;
    }
    for (i=1; i<=p; i++) f[x][i]=res[rk][i-1];
}
 int main()
{
    scanf("%d%d",&n,&p);
    int u,v;
    for (int i=1; i<n; i++) 
    {
        scanf("%d%d",&u,&v);
        nx[v]=a[u]; a[u]=v;
        flag[v]=true;
    }
    for (int i=1; i<=n; i++)
        if (flag[i]==false) {root=i; break;}
    dfs(root);
    for (int i=1; i<=n; i++)
        if (i==root) ans=min(ans,f[i][p]);
        else ans=min(ans,f[i][p]+1);
    printf("%d\n",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/lyxzhz/p/11403938.html