poj 2057 树形dp+贪心(蜗牛找家的期望值)
2015-09-30 12:09
274 查看
题意:一只小蜗牛爬树的时候从树枝的末端(也就是从叶子结点)上掉了下来,但是它的壳子还留在上面。于是它从根节点去寻找它的壳子(但是它完全忘记了之前走过的路)。在路途中有些节点上可能住着虫子,虫子可以告诉小蜗牛它之前来没来过。假如壳子在每个叶子节点上的概率相等,为蜗牛选出一条路,使得所需要走的路程的期望最小。
思路:树形dp+贪心,完全自己想出来的,开心!!
显然可以构造状态dp[x][0]表示目的地不在以x为根的子树上需要走的路程期望(相当于遍历这个子树走过的路程),dp[x][1]表示从x出发,且目的地在以x为根的子树上走的路程期望。显然如果x上有虫子,那么dp[x][0]=0;否则dp[x][0] = sum(dp[y][0]+2) , y是x的儿子;
那么求dp[x][1]的时候涉及的问题就是儿子的访问顺序,如果考虑全排列,题目给了每个结点最多有8个儿子,那么复杂度是O(8!*n) = O(40,320,000),时限3s,现在想想说不定可以。但是最好的做法当然是找出规律,贪心来选择顺序。
考虑两个连续的儿子i和j,目的地在i所在子树的期望是son(i)*k+dp[i][1](其中k是访问其他儿子没找到经过的路径),目的地在j的期望是son(j)*(k+dp[i][0]+2)+dp[j][1](+2表示从i回到x,再从x到j)。如此再写出j在前i在后的和,两相比较消去相同项得到前者是son(j)*(bi+2),后者是son(i)*(bj+2),那么如果i在前,所要求的条件就是前者小于后者,那么也就是(bi+2)/son(i)要小于(bj+2)/son(j)。这是贪心经典的交换论证方法。也就是说最优的排列应该按照(bi+2)/son(i)的大小来进行排序。
最后,输出dp[1][1]/son[1]即可。
思路:树形dp+贪心,完全自己想出来的,开心!!
显然可以构造状态dp[x][0]表示目的地不在以x为根的子树上需要走的路程期望(相当于遍历这个子树走过的路程),dp[x][1]表示从x出发,且目的地在以x为根的子树上走的路程期望。显然如果x上有虫子,那么dp[x][0]=0;否则dp[x][0] = sum(dp[y][0]+2) , y是x的儿子;
那么求dp[x][1]的时候涉及的问题就是儿子的访问顺序,如果考虑全排列,题目给了每个结点最多有8个儿子,那么复杂度是O(8!*n) = O(40,320,000),时限3s,现在想想说不定可以。但是最好的做法当然是找出规律,贪心来选择顺序。
考虑两个连续的儿子i和j,目的地在i所在子树的期望是son(i)*k+dp[i][1](其中k是访问其他儿子没找到经过的路径),目的地在j的期望是son(j)*(k+dp[i][0]+2)+dp[j][1](+2表示从i回到x,再从x到j)。如此再写出j在前i在后的和,两相比较消去相同项得到前者是son(j)*(bi+2),后者是son(i)*(bj+2),那么如果i在前,所要求的条件就是前者小于后者,那么也就是(bi+2)/son(i)要小于(bj+2)/son(j)。这是贪心经典的交换论证方法。也就是说最优的排列应该按照(bi+2)/son(i)的大小来进行排序。
最后,输出dp[1][1]/son[1]即可。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <queue> using namespace std; #define INF 0x3fffffff #define clr(s,t) memset(s,t,sizeof(s)) #define N 1001 bool ch ; int dp [2],first ,top,son ; int n; struct edge{ int y,next; }e ; struct node{ int a,b,son; }; void add(int x,int y){ e[top].y = y; e[top].next = first[x]; first[x] = top++; } int cmp(node x,node y){ return (x.b+2)*y.son < (y.b+2)*x.son; } void dfs(int x){ int i,j,k,y; struct node p ; j = 0; dp[x][0] = dp[x][1] = 0; if(first[x] == -1){ son[x] = 1; return; } son[x] = 0; for(i = first[x];i!=-1;i=e[i].next){ y = e[i].y; dfs(y); dp[x][0] += dp[y][0]+2; son[x] += son[y]; p[j].a = dp[y][1]; p[j].b = dp[y][0]; p[j++].son = son[y]; } if(ch[x]) dp[x][0] = 0; sort(p,p+j,cmp); for(i = 0,k=1;i<j;i++){ dp[x][1] += p[i].son*k+p[i].a; k += p[i].b+2; } } int main(){ while(scanf("%d",&n) && n){ int i,j; char str; top = 0; clr(first, -1); clr(ch, false); for(i = 1;i<=n;i++){ scanf("%d %c",&j,&str); if(i>1) add(j,i); if(str == 'Y') ch[i] = true; } dfs(1); printf("%.4lf\n",(double)dp[1][1]/son[1]); } return 0; }
相关文章推荐
- Linux学习笔记之 DNS原理介绍、DNS搭建、主从复制、子域授权和视图
- Hadoop之HA验证
- Spring错误返回BindingResult
- mysql多实例添加
- React Native 官方文档中文版
- 不影响原有Eclipse ADT环境安装Android Studio注意事项
- hibernate.hbm2ddl.auto(自动创建表结构)配置详解
- Mecanim Animator使用详解
- Linux netstat命令详解
- shell——wget命令
- Android启动脚本init.rc(2)
- linux下mysql远程连接
- 第3章 数据链路
- Unique Binary Search Trees II
- HTML5秘籍---第一章(HTML5简介)
- go语言内存分配之TCMalloc
- 实现水平listview,而且解决水平listview在scrollview中出现的滑动冲突
- 讨论嵌入式系统测试方案
- 常用工具设置互联网代理
- ubuntu下使用终端命令行上网的方法