Poj 3659 Perfect Service【最小支配集、树型dp】

Perfect Service

Time Limit: 2000MS

 

Memory Limit: 65536K

Total Submissions: 1499

 

Accepted: 726

Description

A network is composed of N computers connected by N − 1 communication links such that any two computers can be communicated via a unique route. Two computers are said to be adjacent if there is a communication link between them. The neighbors of a computer is the set of computers which are adjacent to it. In order to quickly access and retrieve large amounts of information, we need to select some computers acting as servers to provide resources to their neighbors. Note that a server can serve all its neighbors. A set of servers in the network forms a perfect service if every client (non-server) is served by exactly one server. The problem is to find a minimum number of servers which forms a perfect service, and we call this number perfect service number.

We assume that N (≤ 10000) is a positive integer and these N computers are numbered from 1 to N. For example, Figure 1 illustrates a network comprised of six computers, where black nodes represent servers and white nodes represent clients. In Figure 1(a), servers 3 and 5 do not form a perfect service because client 4 is adjacent to both servers 3 and 5 and thus it is served by two servers which contradicts the assumption. Conversely, servers 3 and 4 form a perfect service as shown in Figure 1(b). This set also has the minimum cardinality. Therefore, the perfect service number of this example equals two.

Your task is to write a program to compute the perfect service number.

Input

The input consists of a number of test cases. The format of each test case is as follows: The first line contains one positive integer, N, which represents the number of computers in the network. The next N − 1 lines contain all of the communication links and one line for each link. Each line is represented by two positive integers separated by a single space. Finally, a 0 at the (N + 1)th line indicates the end of the first test case.

The next test case starts after the previous ending symbol 0. A −1 indicates the end of the whole inputs.

Output

The output contains one line for each test case. Each line contains a positive integer, which is 
the perfect service number.

Sample Input

6

1 3

2 3

3 4

4 5

4 6

0

2

1 2

-1

Sample Output

2

1

Source

Kaohsiung 2006

 

题目大意:有n个电脑,有n-1条边连接电脑,我们为了更好的共享电脑之间的数据,我们需要设立一些服务器,一台服务器可以管理其自己和直接相连边的电脑,问在不能有一台非服务器电脑被两台服务器所管理的条件下,最少设立多少台服务器可以达到所有电脑都有所覆盖的数目。


思路:明显的最小支配集问题,我们用树型dp来做这个题,


思路:这是个经典的最小支配集的问题,可以用贪心来解决,也可以用树型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属于支配集,但是其子节点不能被两个点覆盖,所以这种情况下,其子节点要么是属于支配集的点,要么是没有被其子节点的子节点有所覆盖的点,辣么不难理解其状态转移方程为:

dp【u】【0】+=1+枚举所有子节点min(dp【v】【0】,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、根节点的确定:我们不妨建立成双向图,那么任何一个点都可以做为根节点向下递归求解。


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],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);
        }
        int op;
        scanf("%d",&op);
        Dp(1);
        printf("%d\n",min(min(dp[1][0],dp[1][1]),dp[1][2]+1));
        if(op==-1)break;
    }
}







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值