DP中的树上边/点覆盖问题

树上边覆盖问题

例:luoguP2016 战略游戏

简述题意:

每个节点都能放一个士兵,每个士兵能看守与他相邻的所有边,求覆盖所有边最少需要多少士兵?

Solution:

\(f[x][1/0]\) 表示回溯到第 \(x\) 个点时所用士兵数量, \(1\) 表示在这里放一个士兵, \(0\) 表示不放

显然珂推得转移方程:

\[f[x][1] = 1 + \sum_{v \in son[x]}min(f[v][1],f[v][0]) \]

\[f[x][0] = \sum_{v \in son[x]} f[v][1] \]

Code

#include<iostream>
#include<cstdio>

using namespace std;
const int MAXN = 1610;

struct edge{
    int to, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge;
int n, k;
int f[MAXN][2];

int read(){
    int s = 0, w = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
    return s * w;
}
 
void add_edge(int from, int to){ e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }

void dfs(int x, int fa){
    f[x][1] = 1;
    for(int i = head[x]; i; i = e[i].nxt){
        int v = e[i].to;
        if(v == fa) continue;
        dfs(v, x);
        f[x][1] += min(f[v][1], f[v][0]);
        f[x][0] += f[v][1];
    }
}

int main()
{
    n = read();
    for(int i = 1, x; i <= n; ++i){
        x = read(), x++;
        k = read();
        for(int j = 1, v; j <= k; ++j){
            v = read(), v++;
            add_edge(x, v), add_edge(v, x);
        }
    }

    dfs(1, 0);

    printf("%d", min(f[1][1], f[1][0]));

    return 0;
}

树上点覆盖问题

例:P2458 [SDOI2006]保安站岗

简述题意

每个保安可以保护自己的点和相邻点,求将树上所有点都覆盖最少所需保安数

Solution

\(f[u][0/1/2]\) 表示回溯到第 \(u\) 个点时所用士兵数量, \(0\) 表示在自己这里放一个士兵, \(1\) 表示被儿子覆盖, \(2\) 表示被父亲覆盖,
(其中 \(x\) 点是用来覆盖它父亲 \(u\) 的点)

转移方程:

\[f[u][0] = \sum_{v \in son[u]} min(f[v][0],f[v][1],f[v][2]) \]

\[f[u][1] = f[x][0] + \sum_{v \in son[u] \And \And v != x} min(f[v][0], min[v][1]) \]

\[f[u][2] = \sum_{v \in son[u]} min(f[v][0], f[v][1]) \]

感觉 \(f[u][0]\)\(f[u][2]\) 的转移方程都比较显然

对于 \(f[u][1]\) 因为不同于树上边覆盖问题,它可以被自己的儿子覆盖,所以对儿子的要求是:要么是被自己覆盖,要么被自己的儿子覆盖

但对于 \(u\) 本身要保证自己的儿子中有一个是被自己覆盖,所以要求出最优的那个儿子 \(x\) 就好了,可以枚举所有儿子,这里介绍一种数学式子优化

最优的 \(x\) 满足 \(f[x][0] - min(f[x][0], f[x][1])\) 最小

参考题解

证明:

因为 \(x\) 满足 \(f[u][1] = f[x][0] + \sum_{v \in son[u] \And \And v != x} min(f[v][0], min[v][1])\)

\(F(u, x) = f[x][0] + \sum_{v \in son[u] \And \And v != x} min(f[v][0], min[v][1])\)

假设 \(x\) 不是最优的, 则必有一个 \(y\) 满足 \(F(u, x) > F(u, y)\)

将这个式子化简得(可以将相同的部分消掉)

\(f[x][0] - min(f[x][0], f[x][1]) > f[y][0] - min(f[y][0], f[y][1])\)

所以有最优的 \(x\) 满足 \(f[x][0] - min(f[x][0], f[x][1])\) 最小

证毕

下面是代码时间:

Code:

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct edge{
    int to, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge = 0;

int n, m;
int f[MAXN][3];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

void add_edge(int from, int to) { e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }

void dfs(int u, int fa) {
    int x = 0;
    for(int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if(v == fa) continue;
        dfs(v, u);
        f[u][0] += min(f[v][0], min(f[v][1], f[v][2]));
        f[u][2] += min(f[v][0], f[v][1]);
        if(f[x][0] - min(f[x][0], f[x][1]) > f[v][0] - min(f[v][0], f[v][1])) x = v;
    }
    f[u][1] = f[x][0];
    for(int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if(v == fa || v == x) continue;
        f[u][1] += min(f[v][0], f[v][1]); 
    }
}

int main()
{
    n = read();
    for(int i = 1, u; i <= n; ++i) {
        u = read(), f[u][0] = read(), m = read();
        for(int j = 1, v; j <= m; ++j) {
            v = read();
            add_edge(u, v), add_edge(v, u);
        }
    }
    f[0][0] = INF;
    dfs(1, 0);
    printf("%d", min(f[1][0], f[1][1]));
    return 0;
}

其他两个树上点覆盖问题例题,稍微改一下输入即可,一个套路

P2899 [USACO08JAN]Cell Phone Network G

T155737 搬书

最后欢迎大家来补充啊,团队私题要是涉及隐私的话可以联系我移除

posted @ 2020-12-02 15:19  Suzt_ilymtics  阅读(252)  评论(0编辑  收藏  举报