其他分享
首页 > 其他分享> > 牛客假日团队赛8:H.Cell Phone Network(最小支配集)

牛客假日团队赛8:H.Cell Phone Network(最小支配集)

作者:互联网

链接:https://ac.nowcoder.com/acm/contest/1069/A
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
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 ≤ N; A ≠ B) there is a sequence of adjacent pastures such that A 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.
输入描述:

5
1 3
5 2
4 3
3 5

输出
复制

2

说明
The towers can be placed at pastures 2 and 3 or pastures 3 and 5.

一.题意:

给出直接相连的几条边,选择最少的点,使所有点都被覆盖(点能被覆盖的条件是:与所选点 有边直接相连)
这题实质上是求最小支配集

二.最小支配集的概念:

支配集的定义如下:给定无向图G =(V , E),其中V是点集, E是边集, 称V的一个子集S称为支配集当且仅当对于V-S中任何一个点v, 都有S中的某个点u, 使得(u, v) ∈E。
特别地,最小的支配集S(即任意一个比S小的集合都不可能是支配集)叫做最小支配集

三 .求解最小支配集的方法:

1.类似Tarjan且基于dfs的贪心
2.树形dp

四.具体操作

解法一:
贪心:
1.选择其中一个点作为root,定义father数组,并标记root的父亲为自己。
2.定义一个dfsn数组,用于dfs时记录每个点dfs序
3.dfs,dfs得到dfs序和每个点的father
4.定义一个s数组,作为支配集容器,
重新初始化vis,vis用于标记是否属于支配集和是否与支配集中的点相连
记录答案:按照dfs序的逆序依次选点
对于一个即不属于支配集也不与支配集中的点相连的点来说
,如果他的父节点不属于支配集,将其父节点加入到支配集,ans++,标记当前节点父节点属于支配集s
标记当前结点、当前结点的父节点(属于支配集)、当前结点的父节点的父节点(与支配集中的点相连)。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e4+5;
vector<int>vec[MAXN];
int dfn[MAXN];
bool vis[MAXN];
int father[MAXN];
int s[MAXN];
int cnt;
int n;
void init()
{
    for(int i = 0; i<= n; i++)
    {
        vis[i] = false;
    }
}
void dfs(int s)
{
    dfn[++cnt] = s ;
    int iSize = vec[s].size();
    for(int i = 0; i < iSize; i++)
    {
        int to = vec[s][i];
        if(!vis[to])
        {
            father[to] = s;
            vis[to] = true;
            dfs(to);
        }
    }
    return;
}
int solve()
{
    father[1] = 1;
    vis[1] = true;
    dfs(1);
    init();
    int ans = 0;
    for(int i = cnt; i > 0; i--)
    {
        int x = dfn[i];
        if(!vis[x])//不属于支配集也不与支配集中的点相连
        {
            if(!s[father[x]])//它的父节点不属于支配集
            {
                ans++;
                s[father[x]] = true;//父节点入支配集
            }
            vis[x] = true;//标记自己
            vis[father[x]] = true;//标记自己的父亲
            vis[father[father[x]]] = true;//标记自己父亲的父亲
        }
    }
    return ans;
}
int main()
{
    cin>>n;
    int x,y;
    for(int i = 1; i < n; i++)
    {
        cin>>x>>y;
        vec[x].push_back(y);
        vec[y].push_back(x);
    }
    cout<<solve()<<endl;
    return 0;
}

解法二:
dp:(我表示并不会)
请参考下面来自百度百科的代码:

#include<iostream> 
#include<cstdlib> 
#include<cstdio> 
#include<cmath> 
using namespace std; 
const int MAXN=10010; 
const int INF=int(1e7); 
struct node 
{ 
    int to; 
    int next; 
} edge[MAXN*2]; 
int head[MAXN*2],num; 
int dp[MAXN][3]; 
/* 
1):dp[i][0],表示点 i 属于支配集合,
并且以点 i 为根的子树都被覆盖了的情况下支配集中所包含最少点的个数. 
2):dp[i][1],表示点 i 不属于支配集合,且以 i 为根的子树都被覆盖,
且 i 被其中不少于一个子节点覆盖的情况下支配集所包含最少点的个数. 
3):dp[i][2],表示点 i 不属于支配集合,
且以 i 为根的子树都被覆盖,且 i 没被子节点覆盖的情况下支配集中所包含最少点的个数.即 i 将被父节点覆盖. 
*/ 
int n; 
void add(int x,int y) 
{ 
    edge[++num].next=head[x]; 
    edge[num].to=y; 
    head[x]=num; 
} 
void DP( int k,int fat ) 
{ 
    dp[ k ][ 0 ]=1; 
    dp[ k ][ 2 ]=0; 
    bool s=0; 
    int x,sum=0,inc=INF; 
    for(int i=head[ k ]; i ;i=edge[i].next ) 
    { 
        x=edge[i].to; 
        if( x==fat )    continue ; 
        DP( x,k ); 
        dp[ k ][ 0 ]+=min(dp[ x ][ 0 ],min(dp[ x ][ 1 ],dp[ x ][ 2 ])); 
        if( dp[ x ][ 0 ] <= dp[ x ][ 1 ] ) 
        { 
            sum+=dp[ x ][ 0 ]; 
            s=1; 
        } 
        else 
        { 
            sum+=dp[ x ][ 1 ]; 
            inc=min(inc,dp[ x ][ 0 ]-dp[ x ][ 1 ]); 
        } 
        if( dp[ x ][ 1 ]!=INF && dp[ k ][ 2 ]!=INF ) 
        //x不是叶子节点,而且k有父亲,即k不是根  
            dp[ k ][ 2 ]+=dp[ x ][ 1 ]; 
        else 
            dp[ k ][ 2 ]=INF; 
    } 
    if( inc==INF && !s )//k没有子节点     
        dp[ k ][ 1 ]=INF; 
    else 
    { 
        dp[ k ][ 1 ]=sum; 
        if( !s )//如果不为零,则k的子节点中有一个已经染色     
            dp[ k ][ 1 ]+=inc; 
    } 
    return ; 
} 
int main() 
{ 
    int u,v; 
    scanf("%d",&n); 
    for(int i=1; i<=n-1; i++) 
    { 
        scanf("%d%d",&u,&v); 
        add(u,v); 
        add(v,u); 
    } 
    DP( 1,0 ); 
    int ans=min(dp[1][0],dp[1][1]); 
    printf("%d\n",ans); 
    return 0; 

标签:pastures,支配,Network,int,Cell,Phone,MAXN,节点,dp
来源: https://blog.csdn.net/tb_youth/article/details/98841419