[Usaco2002 Feb]Rebuilding Roads重建道路

题目

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个节点的子树的道路的最小数目。

Sample Input

11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11

Sample Output

2

Hint

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

思路

这是一道树形dp的题;

我们设dp[x][i]表示以x为根保留i个节点需要割掉多少边;

那么如果x使叶子节点的话,dp[x][1]=1;(保留这个叶子节点,需要割去上面与它相连的父节点);

然后,我们会发现,每个节点与它相连的就是它的父节点与它的子节点;

那么dp[x][1]=子节点数+1;割去所有与它相连的边就可以保留x这个点;

但是根节点上面使没有父节点与它相连,所以dp[1][1]=子节点数;

dp方程是:

dp[x][i]=min(dp[x][i],dp[xx][j]+dp[x][i-j]-2);

xx是x的son,j枚举儿子节点割去的边数

为什么要减二呢,在初始dp[x][1]时割去了x与xx相连的边,初始dp[xx][1]时也割去了这条边;

所以转移的时候要减二;

代码

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline ll read()
{
    ll a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
ll n,q;
ll head[2010],dp[2010][2100],son[2010],v[2010],dep[2010];
struct ljj
{
    ll stb,to;
}a[2010];
ll s=0;
inline void insert(ll x,ll y)//连边
{
    s++;
    a[s].stb=head[x];
    a[s].to=y;
    head[x]=s;
}
inline void dfs(ll x,ll fa)//遍历
{
    for(ll i=head[x];i;i=a[i].stb)
    {
        ll xx=a[i].to;
        if(xx==fa)
            continue;
        dfs(xx,x);
        for(ll j=q;j>=0;j--)
        for(ll k=0;k<=j;k++)
            dp[x][j]=min(dp[x][j],dp[xx][k]-1+dp[x][j-k]-1);
//在初始dp[x][1]时割去了x与xx相连的边,初始dp[xx][1]时也割去了这条边; //所以转移的时候要减二; // cout<<x<<" "<<j<<" "<<k<<" "<<dp[x][j]<<" "<<dp[xx][k]<<" "<<dp[x][j-k]<<endl; } } int main() {//具体解析看思路 memset(dp,127/3,sizeof(dp));//因为题目求最小值,如果不把数组设为无限大,答案就会是0 n=read();q=read(); for(ll i=1;i<n;i++) { ll x=read(),y=read(); insert(x,y); insert(y,x); son[x]++;//统计每个节点的子节点 } for(ll i=1;i<=n;i++) dp[i][1]=son[i]+1; dp[1][1]=son[1];//根节点上面使没有父节点与它相连,所以dp[1][1]=子节点数; dfs(1,0); ll ans=1<<30; for(ll i=1;i<=n;i++) ans=min(ans,dp[i][q]); printf("%lld\n",ans); }

猜你喜欢

转载自www.cnblogs.com/wzx-RS-STHN/p/13396140.html