Cell Phone Network G

最小点队的题意:https://www.luogu.com.cn/problem/P2899

与战略游戏不同的是,这里要求占领所有的点而不是边。


 

1自己被自己染色(有信号塔)

这时我们可以想一下,u被自己染色可以由什么转移过来,如果u已经被自己染色了的话,他的儿子v可以选择自己染色,也可以选择被自己(vv)的儿子染色,当然也可以被u染色,当然,我们要选最小的,所以转移方程就是

 f [ u ] [ 0 ]  +=  min ( f [ v ] [ 0 ] ,   f  [ v ] [ 1 ] ,  f [ v ] [ 2 ] ) 

2.被自己的父亲结点染色(自己没有信号塔)

如果被父亲结点(fa)染色了,那么u的儿子v只能选择自己染色或者被它的儿子染色,转移方程为

f [ u ] [ 1 ]  += min  ( f [ v ] [ 0 ] ,  f [ v ] [ 2 ] )

3.被自己的儿子结点染色(自己没有信号塔)

这是最麻烦的一种情况,因为u可能有多个儿子,只要有一个儿子自己染色了,就可以将u覆盖,这种情况就成立了。

那我们就用一个flag记号来考察,在转移的时候,是不是曾经从有信号塔的儿子( dp [ v ] [ 0 ] )转移过来

如果有,那皆大欢喜,什么都不用做。

相反,如果没有,就说明所有状态都是由没有信号塔的儿子( dp [ v ] [ 2 ] ) 转移而来

那我们就应该找一个儿子,把那个儿子换成 dp [ v ] [ 0 ]

找哪个儿子呢?当然是 dp [ v ] [ 0 ] - dp [ v ] [ 2 ] 最小的那个儿子

#include <bits/stdc++.h>
using namespace std;
const int maxn=10009;
int n;
vector<int>vec[maxn];
int vis[maxn],dp[maxn][4];
//dp[now][0]自己覆盖自己,放信号塔 
//dp[now][1]父亲覆盖自己,本身不放信号塔 
//dp[now][2]儿子覆盖自己,本身不放信号塔 
void dfs(int now)
{
    int flag=0,p=99999999;
    dp[now][0]=1;
    for(int i=0;i<vec[now].size();i++)
    {
        int w=vec[now][i];
        if(vis[w])    continue;
        vis[w]=1;
        dfs(w);
        dp[now][0]+=min(dp[w][0],min(dp[w][1],dp[w][2]));//儿子随意    
        dp[now][1]+=min(dp[w][0],dp[w][2]);
    //    dp[now][2]+=min(dp[w][0],dp[w][2]);
        if(dp[w][0]<=dp[w][2])
        {
            flag=1;
            dp[now][2]+=dp[w][0];    
        } 
        else
        {
            dp[now][2]+=dp[w][2];
            p=min(p,dp[w][0]-dp[w][2]);
        }
    } 
    if(flag==0)    dp[now][2]+=p;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n-1;i++)
    {
        int l,r;
        cin>>l>>r;
        vec[l].push_back(r);
        vec[r].push_back(l);
    }
    vis[1]=1;
    dfs(1);
    cout<<min(dp[1][2],dp[1][0]);
} 
原文地址:https://www.cnblogs.com/iss-ue/p/12499537.html