题意:Z国有n个城市,从1到n给这些城市编号。城市之间连着高速公路,并且每两个城市之间有且只有一条通路。不同的高速公路可能有不同的长度。最近Z国经常发生火灾,所以当地政府决定在某些城市修建一些消防站。在城市k修建一个消防站须要花费大小为W[k]的费用。函数W对于不同的城市可能有不同的取值。如果在城市k没有消防站,那么它到离它最近的消防站的距离不能超过D[k]。每个城市在不超过距离D[k]的前提下,必须选择最近的消防站作为负责站。函数D对于不同的城市可能有不同的取值。为了节省钱,当地政府希望你用最少的总费用修建一些消防站,并且使得这些消防站满足上述的要求。摘自陈启峰论文,国家集训队论文集2006年

COGS上也有这道题,消防站,不过题目描述给简化了一下,思路少绕一个弯。

感觉这道题很棒,主要是自己并不会,去看了陈启峰的论文,了解到了一种解题的思想,可以去看一看那一篇论文,收获蛮大。

这道题还是很容易看出来用树型动规的,不过要用,还需要一点点变化,因为按照题目描述,想建立一个不错状态、设计出合法的转移是比较困难的。引用论文中的说法就是:题目的要求太严格了。因为要求对于每个城市,要求在D[k]范围内,选择最近的一个消防站作为负责站,因为最近这个词,使得状态设计很艰难,而COGS上简化的题意就直接略去了这个词。所以我们要简化题意、放宽限制。


这也算是通过这篇论文了解到的一种思想吧,当题目要求使得我们不太容易使用已知的算法来解决时,我们可以尝试放宽题目中的一些要求——在不影响解的正确性的前提下;或者说,当题目的要求过于宽泛使得我们无从下手时,我们同样可以在不影响解的正确性的前提下加一些条件。或许这种思想我们做题的时候经常用,不过看了论文是第一次表述出来这种思想。


对于“最近”这个词,思考我们删去后,对解是否有影响?当然没有,假如在范围内有多个消防站,我们解题时按照某一个作为负责站,但是”最近“的仍然是存在的,这一个状态仍然是合法的。
所以,我们可以设计出状态:f[i][j]表示城市i被j城市所建立的消防站负责时,以i为根的子树达到合法局面(每个城市都有相应消防站负责)需要的最小代价。当j不在i的子树中时,w[j]不计算在f[i][j]中。

之后我们就可以设计转移方程,不过当前的形式仍然不容易设计方程,因为这道题不像 vijos 小胖守皇宫 一样,一个节点只有三种情况:被自己看守,被父节点看守,被子节点看守。而且每个节点“被负责”的范围是不同的。比如当前子树i,i被其子树中某一个节点j负责,那么i到j之间的这一段节点被谁负责?这一段仍需要相应的转移,并不好设计。此时的局面就可以说是,要求过于宽泛,无从下手。那么我们需要加一些要求。

假如i被j负责,那么i~j路径上所有的点都被j负责。如果有这个条件,转移就会简单很多,那么这个条件正确与否,是否会影响解的正确性呢?答案是正确的,最优解的局面,必然会有一种情况满足这个条件。一个局面对于多种情况是因为我们之前改了题目的限制,负责站不一定是最近的。详细证明,去看看陈启峰的论文吧。(→.→反正我这篇博客基本是看着那篇论文写的Orz)

对于一个状态f[i][j]首先判断是否合法,即dist(i, j) <= D[i]是否成立,不成立则f[i][j] = INF。
1. 当j不在以i为根的子树内时:
此时对于i节点的每个子节点k都有两个选择,要么选择j为负责站,要么在以k为根的子树中选择负责站。为了节约时间,再开一个best数组,best[i] = min{j在以i为根的子树中 | f[i][j]},转移就是:
f[i][j] = Σ min{best[k], f[k][j]}
2. 当j == i时:
这时对于i的每个子节点k同样是两个选择,同上,只需要最后再加上一个w[i],在i建立消防站的花费即可。转移就是:
f[i][j] = w[i] + Σ min{best[k], f[k][j]}
3. 当j在以i为根的子树内且j != i时:
假如j在以i的子节点k0为根的子树中,那么对于其他子节点k,转移同上,仍然是两种,而k0的转移就只有一种f[k0][j]了。转移就是:
f[i][j] = f[k0][j] + Σ min{best[k], f[k][j]} (k != k0)

现在已经调好了,两个DP函数都是可以用的,不过我最初写的那种会慢一点,因为多一个dfs求每个点的子树中都有那些节点。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>
#include <vector>
using namespace std;int n, w[1005], d[1005], best[1005], dis[1005][1005], f[1005][1005];bitset <1005> B[1005];
vector <int> G[1005];struct Edge{int a, b, c;}E[1005];int get_v(int u, int i)
{return E[G[u][i]].a == u ? E[G[u][i]].b : E[G[u][i]].a;
}void dfs(int u, int fa)
{B[u][u] = 1;for(int i = 0; i < G[u].size(); i++){int v = get_v(u, i);if(v != fa) {dfs(v, u);B[u] |= B[v];}}
}void dfs2(int beg, int end, int u, int fa, int len)
{dis[beg][u] = dis[u][beg] = len;for(int i = 0; i < G[u].size(); i++){int v = get_v(u, i);if(v != fa && !dis[u][v]) dfs2(beg, end, v, u, len+E[G[u][i]].c);}
}int dist(int i, int j)
{if(i == j || dis[i][j]) return dis[i][j];dfs2(i, j, i, 0, 0);return dis[i][j];
}void DP(int u, int fa)
{for(int i = 0; i < G[u].size(); i++){int v = get_v(u, i);if(v != fa) DP(v, u);}for(int j = 1; j <= n; j++) if(dist(u, j) <= d[u])  {f[u][j] = 0;if(B[u][j] && u != j){int k0;for(int k = 0; k < G[u].size(); k++){int l = get_v(u, k);if(B[l][j] && l != fa){k0 = l; break;}}for(int k = 0; k < G[u].size(); k++){int l = get_v(u, k);if(l != fa && l != k0) f[u][j] += min(best[l], f[l][j]);}f[u][j] += f[k0][j];}else{   for(int k = 0; k < G[u].size(); k++){int l = get_v(u, k);if(l != fa) f[u][j] += min(best[l], f[l][j]);}f[u][j] += (u==j)*w[u];}if(B[u][j]) best[u] = min(best[u], f[u][j]);}
}/*void DP(int u, int fa)
{for(int i = 0; i < G[u].size(); i++){int v = get_v(u, i);if(v != fa) DP(v, u);}for(int i = 1; i <= n; i++) if(dist(u, i) <= d[u]){f[u][i] = w[i];for(int j = 0; j < G[u].size(); j++){int v = get_v(u, j);if(v != fa) f[u][i] += min(best[v], f[v][i]-w[i]);}best[u] = min(best[u], f[u][i]);}
}*/int main()
{int T; scanf("%d", &T);while(T--){memset(B, 0, sizeof B);memset(G, 0, sizeof G);memset(dis, 0, sizeof dis);memset(f, 0x3f, sizeof f);memset(best, 0x3f, sizeof best);scanf("%d", &n);for(int i = 1; i <= n; i++) scanf("%d", w+i);for(int i = 1; i <= n; i++) scanf("%d", d+i);for(int i = 1; i < n; i++){scanf("%d %d %d", &E[i].a, &E[i].b, &E[i].c);G[E[i].a].push_back(i);G[E[i].b].push_back(i);  }dfs(1, 0);DP(1, 0);printf("%d\n", best[1]);}return 0;
}

POJ 2152 树型DP //很棒的题相关推荐

  1. poj 2378 树型dp

    和poj1655那道求树的重心基本上一样的,代码也没多大改动. 详情请见 #include<cstdio> #include<algorithm> #include<cs ...

  2. 其他OJ 树型DP 选课

    在朱全民的PPT介绍的一个树型DP经典题,<选课>,中文题目,不结束 找了很久找到了可以提交的OJ,重庆八中 http://www.cqoi.net:2012/JudgeOnline/pr ...

  3. 【树型DP】BZOJ1564 二叉查找树(noi2009)

    标签: 二叉查找树 [题目描述] 已知一棵特殊的二叉查找树.根据定义,该二叉查找树中每个结点的数据值都比它左儿子结点的数据值大,而比它右儿子结点的数据值小. 另一方面,这棵查找树中每个结点都有一个权值 ...

  4. POJ3342 Party at Hali-Bula(树型DP求最大独立集+唯一解判断)

    题意: 公司参加聚会,要求员工不能和他的上司同时参加,求最多能参加几个人并且判断解是否唯一. 要点: 树型DP的经典题,用dp[u][1]表示选取u的最大值,dp[u][0]表示不选取u的最大值,容易 ...

  5. 蓝桥杯:生命之树【树型dp】

    之前本菜还没学树型dp的时候,下意识地认为这个东西很难,感觉这个东西结合了搜索+dp这两座算法界的大山必定很难,但万万没想到啊,这个东西很像我之前讲的记忆化搜索,甚至我认为,记忆化搜索的一个作用就是将 ...

  6. 【树型DP】加分二叉树

    问题 b: [树型DP]加分二叉树 时间限制: 1 Sec  内存限制: 64 MB 提交: 8  解决: 6 [提交] [状态] [讨论版] [命题人:admin] 题目描述 科技忽略了过程就是魔法 ...

  7. 二叉苹果树(树型DP+背包)

    二叉苹果树 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点).这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号 ...

  8. 虚树+树型DP SDOI2011消耗战

    <虚树+树型DP> SDOI2011消耗战 #include <iostream> #include <cstdio> #include <cstring&g ...

  9. BSOJ 2923:藤原妹红 MST+树型DP

    2923 -- [模拟试题]藤原妹红 Description 在幻想乡,藤原妹红是拥有不老不死能力的人类.虽然不喜欢与人们交流,妹红仍然保护着误入迷途竹林村民.由于妹红算得上是幻想乡最强的人类,对于她 ...

最新文章

  1. 上传Android或Java库到Maven central repository(转载)
  2. mysql 多个游标_mysql 存储过程中使用多游标
  3. PHP弱类型及一些绕过姿势
  4. php超市结算,超市物品结算简易程序代码
  5. adb echo shell 覆盖_一次写shell脚本的经历记录
  6. java中常量定义在interface中好还是定义在class中
  7. input file multiple 配合springmvc实现多文件上传
  8. iView UI常用组件DatePicker清空技巧
  9. paip.使用泛型时未能找到类型或命名空间名称“T
  10. win10系统Nessus下载插件错误
  11. 难得一见的数据库事务异常 Deadlock found when trying to get lock解决办法dao.DeadlockLoserDataAccessException怎么办
  12. lavavel 环境配置 summer版
  13. java如何直接打印数组
  14. 我的jQuery之路(笔记)--6
  15. 从网页到微信小程序开发:一:小程序与普通网页的区别
  16. 虚拟光驱文件bin/cue到iso的转换
  17. 小数除以整数在c语言,小数除法竖式计算的错因分析
  18. 2021 软件测试工具大全
  19. GameFramework篇:AssetsBundle Tools配置
  20. 剑指Offer——联通研究院笔、面试题 (Offer-1已收割)

热门文章

  1. springboot+vue+安卓二手交易平台源码
  2. 深度学习AI美颜系列——人像静态/动态贴纸特效算法实现 | CSDN博文精选
  3. manster网易云音乐大数据分析
  4. 计算机科学主题 一亩三分地 每日答题 题库
  5. 一段用c#操作datatable的代码
  6. 一篇文章带你快速上手Airtest和Poco
  7. 神经网络:训练模型+转化为k210上跑的kmodel
  8. 阿里云主机Linux服务器配置步骤
  9. Java和Java大数据有什么区别?
  10. 重磅 ! CVPR2020最新计算机视觉论文代码分类打包下载