POJ 3659 Cell Phone Network【最小支配集、树型dp】

Cell Phone Network

Time Limit: 1000MS

 

Memory Limit: 65536K

Total Submissions: 6254

 

Accepted: 2240

Description

Farmer John has decided to give each of his cows a cell phone in hopes to encourage their social interaction. This, however, requires him to set up cell phone towers on his N (1 ≤ N ≤ 10,000) pastures (conveniently numbered 1..N) so they can all communicate.

Exactly N-1 pairs of pastures are adjacent, and for any two pastures A and B (1 ≤ A ≤ N; 1 ≤ B ≤ NA ≠ B) there is a sequence of adjacent pastures such that is the first pasture in the sequence and B is the last. Farmer John can only place cell phone towers in the pastures, and each tower has enough range to provide service to the pasture it is on and all pastures adjacent to the pasture with the cell tower.

Help him determine the minimum number of towers he must install to provide cell phone service to each pasture.

Input

* Line 1: A single integer: N
* Lines 2..N: Each line specifies a pair of adjacent pastures with two space-separated integers: A and B

Output

* Line 1: A single integer indicating the minimum number of towers to install

Sample Input

5

1 3

5 2

4 3

3 5

Sample Output

2

Source

USACO 2008 January Gold

 


题目大意:有n个点,农场主准备给每个点的牛一个电话,如果在某点设立电线杆,那么其相连的点和当前点都可以互相通电话,请问最少设立多少个电线杆才能使得所有点的牛都可以互相打电话。


思路:这是个经典的最小支配集的问题,可以用贪心来解决,也可以用树型dp来搞定,我们这里来详解一下树型dp如何实现。

1、首先既然是dp问题,首先我们要设计dp的数组和其含义:

dp【u】【0】表示点u属于支配集,并且以u为根节点的子树已经全部被覆盖。

dp【u】【1】表示点u不属于支配集,并且以u为根节点的子树已经全部覆盖,并且点u被至少其一个子节点所支配。

dp【u】【2】表示点u不属于支配集,并且以u为根节点的子树已经全部覆盖,并且点u没有被其子节点所支配。(那么不难理解,这个点要被其父节点所支配)


2、对数组进行设计完毕之后,我们就要来考虑状态转移过程,分别讨论三种dp数组是如何转移过来:

无论哪种状态,其想要达到的目的是:以u为根节点,并且包含节点u在内的子树都被支配覆盖


dp【u】【0】:dp【u】【0】表示点u属于支配集,所以其子节点的状态无所厚非,包含u在内的以u为根节点的全部都能被支配覆盖,所以其状态来源和其子节点状态无所相关,我们只要在其子节点中找最优解即可,辣么其状态转移方程:

dp【u】【0】+=1+枚举所有子节点min(dp【v】【0】,dp【v】【1】,dp【v】【2】)


dp【u】【1】:dp【u】【1】表示点u不属于支配集,但是点u被支配着,所以其支配来源一定是从子节点来的,只要其子节点中只要有一个节点属于支配集,那么当前节点u就会被支配,也就是说以u为根节点的子树包含u在内,所有节点都背覆盖了,达到目的。那么不难理解,dp【u】【1】的来源可能是dp【v】【0】或者是dp【v】【1】,并且满足至少一个来源是dp【v】【0】即可。

辣么其状态转移方程:

当然,如果节点u没有子节点:

dp【u】【1】=INF;

如果节点u有子节点:

dp【u】【1】+=枚举所有子节点min(dp【v】【0】,dp【v】【1】)//并且保证其状态转移过程中至少有一个来源于dp【v】【0】,具体参考代码实现。


dp【u】【2】:dp【u】【2】表示点u不属于支配集,并且点u不被其任一子节点所支配,那么其来源就只能是dp【v】【1】或者是dp【v】【2】,但是如果来源是dp【v】【2】的话,那么我们就不能保证两个节点被同时支配了,所以dp【u】【2】只能来源于dp【v】【1】。

d辣么其状态转移方程:
dp【u】【2】+=枚举所有子节点dp【v】【1】;其中任一dp【v】【1】==INF,那么dp【u】【2】=INF。


辣么不难理解,ans=min(dp【root】【0】,dp【root】【1】,1+dp【root】【2】)


3、根节点的确定:我们不妨把图建成双向图,然后任取一点作为根节点即可,从根节点开始向下进行dp操作。


思路构建完毕,具体细节实现,参考代码来实现。


AC代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define INF 0x1f1f1f1f
int head[100000];
struct EdgeNode
{
    int to;
    int w;
    int next;
}e[100000];
int cont;
int dp[5000000][3];
int vis[500000];
void add(int from,int to)
{
    e[cont].to=to;
    e[cont].next=head[from];
    head[from]=cont++;
    e[cont].to=from;
    e[cont].next=head[to];
    head[to]=cont++;
}
void Dp(int u)
{
    dp[u][2]=0;
    dp[u][1]=0;
    dp[u][0]=1;
    int sum=0,inc=INF;
    vis[u]=1;
    bool judge=false;
    for(int k=head[u];k!=-1;k=e[k].next)
    {
        int to=e[k].to;
        if(vis[to]==1)continue;
        Dp(to);
        dp[u][0]+=min(dp[to][0],min(dp[to][1],dp[to][2]));
        if(dp[to][0]<=dp[to][1])
        {
            sum+=dp[to][0];
            judge=true;
        }
        else
        {
            sum+=dp[to][1];
            inc=min(inc,dp[to][0]-dp[to][1]);
        }
        if(dp[to][1]!=INF&&dp[u][2]!=INF)dp[u][2]+=dp[to][1];
        else dp[u][2]=INF;
    }
    if(inc==INF&&judge==false)dp[u][1]=INF;
    else
    {
        dp[u][1]=sum;
        if(judge==false)dp[u][1]+=inc;
    }
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        memset(dp,0,sizeof(dp));
        cont=0;
        for(int i=0;i<n-1;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        Dp(1);
        printf("%d\n",min(min(dp[1][0],dp[1][1]),dp[1][2]+1));
    }
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值