树上边覆盖问题

例: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;
// cout<<x<<endl;
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\) 表示被父亲覆盖

转移方程:

\[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> using namespace std;
const int MAXN = 1e6+6;
const int inf = 0x3f3f3f3f;
struct edge{
int to, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge; 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;
} int n;
int f[MAXN][3]; void add_edge(int from, int to){
e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge;
} void dfs(int x, int fa){
int sson = 0;
int minn = 988888889;
for(int i = head[x]; i; i = e[i].nxt){
int v = e[i].to;
if(v == fa) continue;
dfs(v, x);
f[x][0] += min(f[v][0], min(f[v][1], f[v][2]));
f[x][2] += min(f[v][0], f[v][1]);
if(f[sson][0] - min(f[sson][0], f[sson][1]) > f[v][0] - min(f[v][0], f[v][1])) sson = v;
}
f[x][1] = f[sson][0];
for(int i = head[x]; i; i = e[i].nxt){
int v = e[i].to;
if(v == fa || v == sson) continue;
f[x][1] += min(f[v][0], f[v][1]);
}
} int main()
{
n = read();
for(int i = 1, m, u, v; i <= n; ++i){
u = read(), f[u][0] = read(), m = read();
for(int j = 1; 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 搬书

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

DP中的树上边/点覆盖问题的更多相关文章

  1. 【bzoj3672】[Noi2014]购票 斜率优化dp+CDQ分治+树的点分治

    题目描述  给出一棵以1为根的带边权有根树,对于每个根节点以外的点$v$,如果它与其某个祖先$a$的距离$d$不超过$l_v$,则可以花费$p_vd+q_v$的代价从$v$到$a$.问从每个点到1花费 ...

  2. HDU 5293 Annoying problem 树形dp dfs序 树状数组 lca

    Annoying problem 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 Description Coco has a tree, w ...

  3. GTK+中的树状列表构件(GtkTreeView)

    GTK+中的树状列表构件(GtkTreeView) GTK+中的树状列表构件(GtkTreeView) 在本章的GTK+程序设计教程中,我们将向大家重点介绍非常常用也有点复杂的构件--GtkTreeV ...

  4. dp乱写2:论dp在不在dp中(但在dp范畴)内的应用

    最近正儿八经的学习了dp,有一些题目非常明显看出来就是dp了比如说:过河卒.方格取数.导弹拦截.加分二叉树.炮兵阵地更加明显的还有:采药.装箱问题.过河.金明的预算方案.今天来谈谈dp的dp在不在dp ...

  5. MySQL中B+树索引的使用

    1)         不同应用中B+树索引的使用 对于OLTP应用,由于数据量获取可能是其中一小部分,建立B+树索引是有异议时的 对OLAP应用,情况比较复杂,因为索引的添加应该是宏观的而不是微观的. ...

  6. oracle中的树状查询

    oracle中的树状查询 工作中经常会遇到将数据库中的数据以树的形式展现的需求.以下我们来看一下该需求在Oracle中如何实现. 首先我们需要有一个树形的表结构(当然有时候会出现表结构不是典型的树形结 ...

  7. C#上移,下移TreeView中的树节点顺序

    C#上移,下移TreeView中的树节点顺序 2009-08-12 20:10 1494人阅读 评论(2) 收藏 举报 c#buttonobjectnullstring C#中,通过单击上移,下移按钮 ...

  8. JavaScript 数据结构与算法之美 - 非线性表中的树、堆是干嘛用的 ?其数据结构是怎样的 ?

    1. 前言 想学好前端,先练好内功,内功不行,就算招式练的再花哨,终究成不了高手. 非线性表(树.堆),可以说是前端程序员的内功,要知其然,知其所以然. 笔者写的 JavaScript 数据结构与算法 ...

  9. 【题解】ARC101F Robots and Exits(DP转格路+树状数组优化DP)

    [题解]ARC101F Robots and Exits(DP转格路+树状数组优化DP) 先删去所有只能进入一个洞的机器人,这对答案没有贡献 考虑一个机器人只能进入两个洞,且真正的限制条件是操作的前缀 ...

随机推荐

  1. Omega System Trading and Development Club内部分享策略Easylanguage源码

    更多精彩内容,欢迎关注公众号:数量技术宅.关于本期分享的任何问题,请加技术宅微信:sljsz01 关于 Omega System Trading and Development Club " ...

  2. 阿里巴巴java开发手册-嵩山版 下载

    引言 今天阿里巴巴开发手册嵩山版又发布了,距离上次泰山版发布才仅仅几个月.是不是有的同学又要感叹下这速度也太快了点吧.我泰山版还没看完,嵩山版直接来了.没看完不要紧,我们直接看嵩山版本就好了.一次性把 ...

  3. Android猜数字大小游戏

    功能介绍:该程序能够提示猜大了猜小了,并且对空白输入处理,还对猜测次数限制,提供重置功能. 1.先看界面,一个输入框EditText,两个Button 2.界面设计  activity_main2.x ...

  4. mysql基础之double,float长度标度定义

    MySQL类型float double decimal的区别 float数值类型用于表示单精度浮点数值,而double数值类型用于表示双精度浮点数值,float和double都是浮点型,而decima ...

  5. 二本非科班,秋招,实习,面试,offer之路

    不知不觉已经工作一年多的,我是2019年7月毕业的,但是如果算上实习就工作差不多两年了的吧. 最近不是刚刚过了圣诞节吗?然后又准备到元旦了,迎来2021年!在微信公众号上看到小部分公众号在总结2020 ...

  6. Android中app开机自启动的开发

    (1)首先添加权限开机启动权限 <!--开机启动权限--> <uses-permission android:name="android.permission.RECEIV ...

  7. node环境使用lowdb轻量数据库以及基本用法

    1.安装: npm install lowdb --save 2..引入lowdb,设置表 const low = require('lowdb');  const FileSync = requir ...

  8. 用C#实现模拟双色球中奖程序 控制台应用程序

    前言 这是我在大一第一学期C#的课程设计,要求编写一个模拟双色球彩票的控制台应用程序,用以实现简单的模拟选购彩票. 一.双色球购号号码生成: 1.系统购号:通过"随机数"产生双色球 ...

  9. UNraid学习随手记:显示主板、CPU传感器温度

    话不多说直接开始 首先安装NerdTools 地址: https://raw.githubusercontent.com/dmacias72/unRAID-NerdPack/master/plugin ...

  10. Databricks 第四篇:分组统计和窗口

    对数据分析时,通常需要对数据进行分组,并对每个分组进行聚合运算.在一定意义上,窗口也是一种分组统计的方法. 分组数据 DataFrame.groupBy()返回的是GroupedData类,可以对分组 ...