题目描述

五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序。

已知整个地下超市的所有通道呈一棵树的形状;某些通道之间可以互相望见。总经理要求所有通道的每个端点(树的顶点)都要有人全天候看守,在不同的通道端点安排保安所需的费用不同。

一个保安一旦站在某个通道的其中一个端点,那么他除了能看守住他所站的那个端点,也能看到这个通道的另一个端点,所以一个保安可能同时能看守住多个端点(树的结点),因此没有必要在每个通道的端点都安排保安。

编程任务:

请你帮助超市经理策划安排,在能看守全部通道端点的前提下,使得花费的经费最少。

输入输出格式

输入:

第\(1\)行 \(n\),表示树中结点的数目。

第\(2\)行至第\(n+1\)行,每行描述每个通道端点的信息,依次为:该结点标号\(i(0<i<=n)\),在该结点安置保安所需的经费\(k(<=10000)\),该边的儿子数\(m\),接下来\(m\)个数,分别是这个节点的\(m\)个儿子的标号\(r1,r2,...,rm\)。

对于一个\(n(0 < n <= 1500)\)个结点的树,结点标号在\(1\)到\(n\)之间,且标号不重复。

输出:

最少的经费。

如右图的输入数据示例

输入输出样例

输入样例#1:

6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0

输出样例#1:

25

说明

样例说明:在结点\(2,3,4\)安置\(3\)个保安能看守所有的\(6\)个结点,需要的经费最小:\(25\)


根据题目描述很容易想到是树上\(DP\),但是有很多需要考虑到的东西:

首先容易被坑的一点:

  • 很可能会直接想到两种状态:当前点占用和不占用。
  • 在这种情况下会很容易就作出如下的判断
    • \(F[u][1]+=min\{F[v][0],F[v][1]\};\)(当前点占用的转移)
    • \(F[u][0]+=F[v][1];\)(当前点不占用的转移)

然后就会第一时间\(w\)\(\bar a\)掉这个题目。

为什么呢?因为这个转移中,我们忽略了子节点对父节点的影响,子节点的选中也可以控制父节点,所以我们的状态应该有三个:

  • 当前点被占用(子节点选啥都可以,直接取最小值)
  • 当前点没有被占用(父节点占用并控制该点,所以可以把子节点里面已经独立可用的都挖过来)
  • 当前点没有被占用,但是子节点中存在有已经被占用的点
    • 这个转移相对不是很好考虑,怎么确认子节点中谁被占用呢?
    • 我们先考虑不管子节点有没有被占用的,直接先取两种转移状态最小值临时存起来
    • 然后一一比较,看哪一个子节点变成占用态消耗最少,答案取min
    • 复杂度\(O(n)\)

实际上算是树上\(DP\)的一种常用套路吧,不过本蒻以前没有掌握,被这个题目好好教育了一下。

Code:

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 1510
#define INF 0x3f3f3f3f
using namespace std;
int cnt,head[MAXN],deep[MAXN];
int n,vis[MAXN],arr[MAXN],size[MAXN],f[MAXN][4];
struct edge{
int nxt;
int to;
}e[MAXN<<1];
void dfs(int u,int fa){
f[u][1]=arr[u];//预先处理被占用的情况
int sum=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v!=fa){
//对子树的处理
dfs(v,u);
f[u][1]+=min(f[v][1],min(f[v][2],f[v][3]));
sum+=min(f[v][1],f[v][2]);
}
}
f[u][3]=sum,f[u][2]=INF;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v!=fa){
f[u][2]=min(f[u][2],sum-min(f[v][1],f[v][2])+f[v][1]);
}
}
// printf("f[%d][1]=%d f[%d][2]=%d f[%d][3]=%d\n",u,f[u][1],u,f[u][2],u,f[u][3]);
return;
} inline void add(int from,int to){
e[++cnt].nxt=head[from];
e[cnt].to=to;
head[from]=cnt;
} int main(){
int u,v,m;
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&u);
scanf("%d%d",&arr[u],&m);
for(int j=1;j<=m;++j){
scanf("%d",&v);
add(u,v);
add(v,u);
}
}//arr记录价格
//f[i][1]->本节点占用时子树最小花费
//f[i][2]->本节点不占用但被控制时子树最小花费
//f[i][3]->本节点完全不受控制时子树最小花费
dfs(1,0);
// for(int i=1;i<=n;++i){
// printf("size %d = %d\n",i,size[i]);
// }
// size保存子树大小
printf("%d\n",min(f[1][1],f[1][2]));
return 0;
}

洛谷【P2458】[SDOI2006]保安站岗 题解 树上DP的更多相关文章

  1. C++ 洛谷 P2458 [SDOI2006]保安站岗 from_树形DP

    P2458 [SDOI2006]保安站岗 没学树形DP的,看一下. 题目大意:一棵树有N个节点,现在需要将所有节点都看守住,如果我们选择了节点i,那么节点i本身,节点i的父亲和儿子都会被看守住. 每个 ...

  2. 洛谷 P2458 [SDOI2006]保安站岗

    题目传送门 解题思路: 树形DP 可知一个点被控制有且仅有一下三种情况: 1.被父亲节点上的保安控制 2.被儿子节点上的保安控制 3.被当前节点上的保安控制 我们设dp[0/1/2][u]表示u节点所 ...

  3. Luogu P2458 [SDOI2006]保安站岗【树形Dp】

    题目描述 五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序. 已知整个地下超市的所有通道呈一棵树的形状:某些通道之间可以互 ...

  4. Luogu P2458 [SDOI2006]保安站岗(树形dp)

    P2458 [SDOI2006]保安站岗 题意 题目描述 五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序. 已知整个地下 ...

  5. P2458 [SDOI2006]保安站岗[树形dp]

    题目描述 五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序. 已知整个地下超市的所有通道呈一棵树的形状:某些通道之间可以互 ...

  6. [Luogu][P2458] [SDOI2006]保安站岗

    题目链接 看起来似乎跟最小点覆盖有点像.但区别在于: 最小点覆盖要求所有边在其中,而本题要求所有点在其中. 即:一个点不选时,它的儿子不一定需要全选. 画图理解: 对于这样一幅图,本题中可以这样选择: ...

  7. 洛谷P2602 [ZJOI2010]数字计数 题解 数位DP

    题目链接:https://www.luogu.com.cn/problem/P2602 题目大意: 计算区间 \([L,R]\) 范围内 \(0 \sim 9\) 各出现了多少次? 解题思路: 使用 ...

  8. 洛谷P2657 [SCOI2009]windy数 题解 数位DP

    题目链接:https://www.luogu.com.cn/problem/P2657 题目大意:找区间 \([A,B]\) 范围内 不含前导零 且 相邻两个数字之差至少为2 的正整数的个数. 题目分 ...

  9. 洛谷P1783 海滩防御 分析+题解代码

    洛谷P1783 海滩防御 分析+题解代码 题目描述: WLP同学最近迷上了一款网络联机对战游戏(终于知道为毛JOHNKRAM每天刷洛谷效率那么低了),但是他却为了这个游戏很苦恼,因为他在海边的造船厂和 ...

随机推荐

  1. php ajax登录注册

    用户登录与退出功能应用在很多地方,而在有些项目中,我们需要使用Ajax方式进行登录,登录成功后只刷新页面局部,从而提升了用户体验度.本文将使用PHP和jQuery来实现登录和退出功能. 准备数据库 本 ...

  2. 《linux内核设计与实现》第五章

    第五章 系统调用 一.与内核通信 系统调用在用户空间进程和硬件设备之间添加了一个中间层.作用: 为用户空间提供了一种硬件的抽象接口. 系统调用保证了系统的稳定和安全. 每个进程都运行在虚拟系统中,而在 ...

  3. Linux内核分析——可执行程序的装载

    链接的过程 首先运行C预处理器cpp,将C的源程序(a.c)翻译成ASCII码的中间文件(a.i) 接着C编译器ccl,将a.i翻译成ASCII汇编语言文件a.s 接着运行汇编器as,将a.s翻译成可 ...

  4. 学习github心得

    Git 是 Linux 的创始人 Linus Torvalds 开发的开源和免费的版本管理系统,利用底层文件系统原理进行版本控制的工具.Git是目前为止最著名运用最好最受欢迎的分布式的配置管理工具. ...

  5. 男神女神配——alpha阶段总结

    一.需求分析 虽然公共社交网络系统能够满足大多数高校校园用户在校园网络社交的需求,但是针对校园学习.工作和文化生活等方面的支持以及学校个性化需求方面却存在不足.利用电子校务平台的数据,设计了与真实校园 ...

  6. ASP.NET MVC随记汇总

    1.学习教程: 1.ASP.NET MVC4入门教程:Asp.Net MVC4入门指南 2.ASP.NET MVC4系类教程 3.ASP.NET MVC学习系列 4.从零开始学习ASP.NET MVC ...

  7. Linux 检查磁盘性能速度

    1. hdparm 工具: hdparm –t 设备名(/dev/sda1) 2. time dd if=/dev/zero of=/tmp/test.dat bs=1G count=1

  8. PDB自动启动以及Oracle Pfile的参数修改示范

    1. Oracle12c 可以使用PDB的模式进行创建, 但是他一般不会自动启动,所以可以穿件一个触发器进行处理 创建语句 CREATE TRIGGER open_all_pdbs AFTER STA ...

  9. [读书笔记]SQLSERVER企业级平台管理实践读书笔记01

    1. SQLSERVER信息收集 SQLDIAG 使用界面 C:\Users\Administrator>sqldiag2018/01/02 08:13:26.10 SQLDIAG Collec ...

  10. ArrayMap代码分析

    Java提供了HashMap,但是HashMap对于手机端而言,对内存的占用太大,所以Android提供了SparseArray和ArrayMap.二者都是基于二分查找,所以数据量大的时候,最坏效率会 ...