POJ 2152 树型DP //很棒的题
题意: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 //很棒的题相关推荐
- poj 2378 树型dp
和poj1655那道求树的重心基本上一样的,代码也没多大改动. 详情请见 #include<cstdio> #include<algorithm> #include<cs ...
- 其他OJ 树型DP 选课
在朱全民的PPT介绍的一个树型DP经典题,<选课>,中文题目,不结束 找了很久找到了可以提交的OJ,重庆八中 http://www.cqoi.net:2012/JudgeOnline/pr ...
- 【树型DP】BZOJ1564 二叉查找树(noi2009)
标签: 二叉查找树 [题目描述] 已知一棵特殊的二叉查找树.根据定义,该二叉查找树中每个结点的数据值都比它左儿子结点的数据值大,而比它右儿子结点的数据值小. 另一方面,这棵查找树中每个结点都有一个权值 ...
- POJ3342 Party at Hali-Bula(树型DP求最大独立集+唯一解判断)
题意: 公司参加聚会,要求员工不能和他的上司同时参加,求最多能参加几个人并且判断解是否唯一. 要点: 树型DP的经典题,用dp[u][1]表示选取u的最大值,dp[u][0]表示不选取u的最大值,容易 ...
- 蓝桥杯:生命之树【树型dp】
之前本菜还没学树型dp的时候,下意识地认为这个东西很难,感觉这个东西结合了搜索+dp这两座算法界的大山必定很难,但万万没想到啊,这个东西很像我之前讲的记忆化搜索,甚至我认为,记忆化搜索的一个作用就是将 ...
- 【树型DP】加分二叉树
问题 b: [树型DP]加分二叉树 时间限制: 1 Sec 内存限制: 64 MB 提交: 8 解决: 6 [提交] [状态] [讨论版] [命题人:admin] 题目描述 科技忽略了过程就是魔法 ...
- 二叉苹果树(树型DP+背包)
二叉苹果树 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点).这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号 ...
- 虚树+树型DP SDOI2011消耗战
<虚树+树型DP> SDOI2011消耗战 #include <iostream> #include <cstdio> #include <cstring&g ...
- BSOJ 2923:藤原妹红 MST+树型DP
2923 -- [模拟试题]藤原妹红 Description 在幻想乡,藤原妹红是拥有不老不死能力的人类.虽然不喜欢与人们交流,妹红仍然保护着误入迷途竹林村民.由于妹红算得上是幻想乡最强的人类,对于她 ...
最新文章
- 上传Android或Java库到Maven central repository(转载)
- mysql 多个游标_mysql 存储过程中使用多游标
- PHP弱类型及一些绕过姿势
- php超市结算,超市物品结算简易程序代码
- adb echo shell 覆盖_一次写shell脚本的经历记录
- java中常量定义在interface中好还是定义在class中
- input file multiple 配合springmvc实现多文件上传
- iView UI常用组件DatePicker清空技巧
- paip.使用泛型时未能找到类型或命名空间名称“T
- win10系统Nessus下载插件错误
- 难得一见的数据库事务异常 Deadlock found when trying to get lock解决办法dao.DeadlockLoserDataAccessException怎么办
- lavavel 环境配置 summer版
- java如何直接打印数组
- 我的jQuery之路(笔记)--6
- 从网页到微信小程序开发:一:小程序与普通网页的区别
- 虚拟光驱文件bin/cue到iso的转换
- 小数除以整数在c语言,小数除法竖式计算的错因分析
- 2021 软件测试工具大全
- GameFramework篇:AssetsBundle Tools配置
- 剑指Offer——联通研究院笔、面试题 (Offer-1已收割)