USACO Cell Phone Network

USACO Cell Phone Network

洛谷传送门

JDOJ传送门

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 <= 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.

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

HINT

INPUT DETAILS:

Farmer John has 5 pastures: pastures 1 and 3 are adjacent, as are pastures
5 and 2, pastures 4 and 3, and pastures 3 and 5. Geometrically, the
farm looks like this (or some similar configuration)

               4  2
               |  |
            1--3--5

OUTPUT DETAILS:

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


题解:

节点选择类树形DP。

这类树形DP一般都设好几种互相有着关联的状态,依此相互转移。(啊,我又来瞎YY总结了)

(%QYB以免被骂)

所以按套路设状态:

(dp[i][0/1/2])表示i根子树全被覆盖,且i被自己、父亲、儿子覆盖的最小个数。

转移也比较好想:

[dp[x][0]=summin(dp[son][0],dp[son][1],dp[son][2])\dp[x][1]=summin(dp[son][0],dp[son][2])\dp[x][2]=dp[Son][0]+summin(dp[son][0],dp[son][2]) ]

重点说一下最后这种状态,注意大小写。根据我们的状态,如果这个点被儿子覆盖,那么必然有一个儿子是被选的。即得先选一个儿子覆盖自己,剩下的其他儿子再正常累加。

那么,如何挑选出这个必须选的Son呢?

很显然,想要保证(dp[x][2])最优,需要使得这个Son的选择也最优。那么如果当前的选择是最优的,肯定会满足:

[dp[Son_1][0]+summin(dp[son][0],dp[son][2])<dp[Son_2][0]+summin(dp[son][0],dp[son][2]) ]

把相同项目消去,即有:

[dp[son][0]-min(dp[son][0],dp[son][2])>dp[y][0]-min(dp[y][0],dp[y][2]) ]

然后就有代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e4+10;
int n;
int tot,to[maxn<<1],nxt[maxn<<1],head[maxn];
int dp[maxn][3];
//dp[i][0/1/2]表示i根子树全被覆盖,且i被自己、父亲、儿子覆盖的最小价值。
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void dfs(int x,int f)
{
    int son=0;
    dp[x][0]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==f)
            continue;
        dfs(y,x);
        dp[x][0]+=min(min(dp[y][0],dp[y][1]),dp[y][2]);
        dp[x][1]+=min(dp[y][0],dp[y][2]);
        if(!son||dp[son][0]-min(dp[son][0],dp[son][2])>dp[y][0]-min(dp[y][0],dp[y][2]))
            son=y;
    }
    if(son)
        dp[x][2]=dp[son][0];
    else
        dp[x][2]=998244353;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==f||y==son)
            continue;
        dp[x][2]+=min(dp[y][0],dp[y][2]);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(1,0);
    printf("%d",min(dp[1][0],dp[1][2]));
    return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/13814785.html