题目背景

四次死亡轮回后,昴终于到达了贤者之塔,当代贤者夏乌拉一见到昴就上前抱住了昴“师傅!你终于回来了!你有着和师傅一样的魔女的余香,肯定是师傅”。
众所周知,大贤者是嫉妒魔女沙提拉的老公,400年前与神龙、剑圣一起封印魔女因子暴走的莎缇拉。在魔女茶会的时候,莎缇拉也表示过对昴浓浓的爱意,昴便是被莎缇拉召唤来异世界的。
而贤者之塔中的资料与试炼,似乎都指向同一种可能性……记忆的轮廓,逐渐显形……

题目描述

通往贤者之塔的路上,有许多的危机。
我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增,在[1,n]中,一共有n个节点。
我们把编号在[1,n]的叫做正确节点,[n+1,m]的叫做错误节点。一个叶子,如果是正确节点则为正确叶子,否则称为错误叶子。
莎缇拉要帮助昴到达贤者之塔,因此现在面临着存档位置设定的问题。为了让昴成长为英雄,因此一共只有p次存档的机会,其中1和n必须存档。被莎缇拉设置为要存档的节点称为存档位置。
当然不能让昴陷入死循环,所以存档只能在正确节点上进行,而且同一个节点不能存多次档。因为通往贤者之塔的路上有影响的瘴气,因此莎缇拉假设昴每次位于树上一个节点时,都会等概率选择一个儿子走下去。每当走到一个错误叶子时,再走一步就会读档。
具体的,每次昴到达一个新的存档位置,存档点便会更新为这个位置(假如现在的存档点是i,现在走到了一个存档位置j>i,那么存档点便会更新为j)。读档的意思就是回到当前存档点。
初始昴位于1,当昴走到正确叶子n时,便结束了路程。莎缇拉想知道,最优情况下,昴结束路程的期望步数是多少?
输入格式

第一行一个正整数T表示数据组数。
接下来每组数据,首先读入三个正整数n,m,p。
接下来m-n行,描述树上所有的非正确边(正确边即连接两个正确节点的边),用两个正整数j,k表示j与k之间有一条连边,j和k可以均为错误节点,也可以一个为正确节点另一个为错误节点。数据保证j是k的父亲。
输出格式

T行每行一个实数表示每组数据的答案。请保留四位小数。

样例输入

1
3 7 2
1 4
2 5
3 6
3 7

样例输出

9.000

数据范围及约定

50%,n=p
70%,50<=p<=n<=500
100%,50<=p<=n<=700,m<=1500,T<=5
数据保证每个除了n的正确节点均有至少2个儿子,至多3个儿子。

---------------------------------------------------------------分界线---------------------------------------------------------------

考试T2,调考前刚qj过改过,确实是一道毒瘤题好题,考试时时间不够看都没看考完试才开始做了这题。

理解题理解了一节课

做题先看数据范围,否则凉凉。

我们可以看到有50%的数据是n=p的,对于n=p的情况,我们不难分析出每个点都存档是最优解,这样情况就简单很多。

接下来我们考虑怎么转移。

设个g[i]为对于一个错误节点i还要走多少步会存档。

g[i]=1+∑g[j]/du[i](j是i的儿子)。一遍dfs就可以处理出来g数组。

我们再处理数组sum,sum[i]=∑g[j](j是i的错误儿子)。

设f[i]表示正确节点i走到n的期望步数,显然f[n]=0,我们倒着递推。
f[i]=1+1/d[i]*f[i+1]+1/d[i]*sigma{g[j]+f[i]}[j是i的错误儿子]
移项得f[i]=d[i]+f[i+1]+s[i]。

over,50pts到手。

接下来我们考虑把它优化到70pts。

设dp(i,j)表示存档点在i还有j次存档机会的最优解。

设a(i,j)表示存档点在i,从i走到正确节点j的最少期望步数。

首先我们可以o(n2)把a数组处理出来。

a(i,j)=a(i,j-1)+1+1/du(j-1)×∑(a(i,j)+g(k)){k是j-1的错误儿子}。

整理移项得a(i,j)=du(i,j-1)×a(i,j-1)+sum(j-1)+du(j-1)。

然后我们枚举存档点k,则dp(i,j)可以由dp(k,j-1)和a(i,k)转移。

时间复杂度O(n2p),70pts到手。

最后我们来考虑正解。其实博主并不会正解。

还是放直链吧。%%%出题人。

https://blog.csdn.net/WerKeyTom_FTD/article/details/53026266

出题人给出了三种正解。

由于第二种看起来十分好写比较优秀,博主选择了第二种。

到现在博主还是很mengbi,在这里就不给予讲解了。

如果有时间的话博主也会用其他两种方法A掉这题的。

下面是三个分数段的代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<cmath>
  6. #include<vector>
  7. const int N=;
  8. using namespace std;
  9. int first[N],nex[N],to[N],tot,vis[N],du[N];double sum[N],g[N],f[N];
  10. void add(int a,int b){
  11. to[++tot]=b;nex[tot]=first[a];first[a]=tot;
  12. }
  13. void dfs(int x){
  14. g[x]=1.0;vis[x]=;
  15. for(int i=first[x];i;i=nex[i]){
  16. int y=to[i];
  17. dfs(y);
  18. g[x]+=1.0/du[x]*g[y];
  19. }
  20. }
  21. int main(){
  22. int T;
  23. scanf("%d",&T);
  24. while(T--){
  25. memset(du,,sizeof(du));
  26. //memset(sum,0,sizeof(sum));
  27. memset(g,,sizeof(g));tot=;
  28. int n,m,p;
  29. scanf("%d%d%d",&n,&m,&p);
  30. for(int i=;i<=m-n;i++){
  31. int a,b;
  32. scanf("%d%d",&a,&b);
  33. add(a,b);
  34. du[a]++;
  35. }
  36. for(int i=;i<=n;i++) du[i]++;
  37. for(int i=n+;i<=m;i++){
  38. if(vis[i]) continue;
  39. dfs(i);
  40. }
  41. for(int i=;i<=n;i++){
  42. sum[i]=0.0;
  43. for(int j=first[i];j;j=nex[j]){
  44. //if(j>n&&j<=m)
  45. if(to[j]>n&&to[j]<=m)
  46. sum[i]+=g[to[j]];
  47. }
  48. }
  49. f[n]=0.0;
  50. for(int i=n-;i>=;i--){
  51. f[i]=f[i+]+sum[i]+du[i];
  52. //cout<<g[i]<<" ";
  53. }
  54. //for(int i=n+1;i<=m;i++) /*cout<<i<<" ",*/printf("%.4lf ",g[i]);
  55. printf("%.4lf\n",f[]);
  56. }
  57. }

50pts

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. #include<cstring>
  5. using namespace std;
  6. #define R register
  7. inline int read(){
  8. R int aa=,bb=;char cc=getchar();
  9. while(cc<''||cc>'')
  10. {if(cc=='-')bb=-;cc=getchar();}
  11. while(cc<=''&&cc>='')
  12. {aa=aa*+cc-'';cc=getchar();}
  13. return aa*bb;
  14. }
  15. const int N=;
  16. const int M=;
  17. struct tree{
  18. int v,last;
  19. }tr[M*];
  20. int tot=,first[M],du[M];
  21. void add(int x,int y){
  22. tr[++tot].v=y;
  23. tr[tot].last=first[x];
  24. first[x]=tot;
  25. du[x]++;
  26. }
  27. int T,n,m,p;
  28. bool vi[M];
  29. double g[M],sum[N],f[N][N],fg[N][N],fi[N];
  30. void dfs(int x){
  31. if(vi[x]) return;
  32. g[x]=1.0; vi[x]=;
  33. for(R int i=first[x],v;i;i=tr[i].last){
  34. v=tr[i].v;
  35. dfs(v);
  36. g[x]+=1.0/du[x]*g[v];
  37. }
  38. }
  39. int main(){
  40. T=read();
  41. while(T--){
  42. memset(vi,,sizeof(vi));
  43. memset(du,,sizeof(du));
  44. memset(first,,sizeof(first));
  45. tot=;
  46. n=read();m=read();p=read();
  47. for(R int i=,x,y;i<=m-n;i++){
  48. x=read();y=read();
  49. add(x,y);
  50. }
  51. for(R int i=;i<=n;i++)du[i]++;
  52. for(R int i=n+;i<=m;i++) if(!vi[i]) dfs(i);
  53. for(R int i=;i<=n;i++){
  54. sum[i]=0.0;
  55. for(R int j=first[i],v;j;j=tr[j].last){
  56. v=tr[j].v;
  57. sum[i]+=1.0*g[v];
  58. }
  59. }
  60. if(n==p){
  61. fi[n]=0.0;
  62. for(R int i=n-;i>=;i--)
  63. fi[i]=(double)(du[i]+fi[i+]+sum[i]);
  64. printf("%.4lf\n",fi[]);
  65. continue;
  66. }
  67.  
  68. for(R int i=;i<=n;i++){
  69. fg[i][i]=0.0;
  70. for(R int j=i+;j<=n;j++){
  71. fg[i][j]=fg[i][j-]*du[j-]+du[j-]+sum[j-];
  72. }
  73. }
  74. for(R int i=;i<=n;i++)
  75. for(R int j=;j<=p;j++)
  76. f[i][j]=0x7ffffff;
  77. for(R int i=;i<=p;i++) f[n][i]=0.0;
  78. for(R int i=n-;i>=;i--){
  79. for(R int j=;j<p;j++){
  80. for(R int k=i+;k<=n;k++){
  81. f[i][j]=min( f[k][j-]+fg[i][k], f[i][j]);
  82. }
  83. }
  84. }
  85. printf("%.4lf\n",f[][p-]);
  86. }
  87. return ;
  88. }

70pts

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. #include<cstring>
  5. using namespace std;
  6. #define R register
  7. inline int read()
  8. {
  9. R int aa=,bb=;char cc=getchar();
  10. while(cc<''||cc>'')
  11. {if(cc=='-')bb=-;cc=getchar();}
  12. while(cc<=''&&cc>='')
  13. {aa=aa*+cc-'';cc=getchar();}
  14. return aa*bb;
  15. }
  16. const int N=;
  17. const int M=;
  18. struct tree{
  19. int v,last;
  20. }tr[M*];
  21. int tot=,first[M],du[M];
  22. void add(int x,int y)
  23. {
  24. tr[++tot].v=y;
  25. tr[tot].last=first[x];
  26. first[x]=tot;
  27. du[x]++;
  28. }
  29. int T,n,m,p;
  30. bool vi[M];
  31. double g[M],sum[N],f[N][N],fg[N][N],fi[N];
  32. void dfs(int x)
  33. {
  34. if(vi[x]) return;
  35. g[x]=1.0; vi[x]=;
  36. for(R int i=first[x],v;i;i=tr[i].last){
  37. v=tr[i].v;
  38. dfs(v);
  39. g[x]+=1.0/du[x]*g[v];
  40. }
  41. }
  42. int main()
  43. {
  44. T=read();
  45. while(T--){
  46. memset(vi,,sizeof(vi));
  47. memset(du,,sizeof(du));
  48. memset(first,,sizeof(first));
  49. tot=;
  50. n=read();m=read();p=read();
  51. for(R int i=,x,y;i<=m-n;i++){
  52. x=read();y=read();
  53. add(x,y);
  54. }
  55. for(R int i=;i<=n;i++)du[i]++;
  56. for(R int i=n+;i<=m;i++) if(!vi[i]) dfs(i);
  57. for(R int i=;i<=n;i++){
  58. sum[i]=0.0;
  59. for(R int j=first[i],v;j;j=tr[j].last){
  60. v=tr[j].v;
  61. sum[i]+=1.0*g[v];
  62. }
  63. }
  64. if(n==p){
  65. fi[n]=0.0;
  66. for(R int i=n-;i>=;i--)
  67. fi[i]=(double)(du[i]+fi[i+]+sum[i]);
  68. printf("%.4lf\n",fi[]);
  69. continue;
  70. }
  71.  
  72. for(R int i=;i<=n;i++){
  73. fg[i][i]=0.0;
  74. for(R int j=i+;j<=n;j++){
  75. fg[i][j]=fg[i][j-]*du[j-]+du[j-]+sum[j-];
  76. }
  77. }
  78. for(R int i=;i<=n;i++)
  79. for(R int j=;j<=p;j++)
  80. f[i][j]=0x7ffffff;
  81. for(R int i=;i<=p;i++) f[n][i]=0.0;
  82. for(R int i=n-;i>=;i--){
  83. for(R int j=;j<p;j++){
  84. int r=min(i+,n);
  85. for(R int k=i+;k<=r;k++){
  86. f[i][j]=min( f[k][j-]+fg[i][k], f[i][j]);
  87. }
  88. }
  89. }
  90. printf("%.4lf\n",f[][p-]);
  91. }
  92. return ;
  93. }

AC

bzoj 4899 记忆的轮廓 题解(概率dp+决策单调性优化)的更多相关文章

  1. [BZOJ4899]:记忆的轮廓(概率DP)

    题目传送门 题目描述: 通往贤者之塔的路上,有许多的危机. 我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增, 在[1,n]中,一共有n个节点.我 ...

  2. [NOI2009]诗人小G(dp + 决策单调性优化)

    题意 有一个长度为 \(n\) 的序列 \(A\) 和常数 \(L, P\) ,你需要将它分成若干段,每 \(P\) 一段的代价为 \(| \sum ( A_i ) − L|^P\) ,求最小代价的划 ...

  3. BZOJ 4899 记忆的轮廓

    话说BZOJ 是不是死了啊 (已经没有传送门了) 设 $f[i][j]$ 表示走到第 $j$ 个位置确定了 $i$ 个存档点时的最小代价,并强制第 $j$ 个位置有一个存档点 那么设 $cst[i][ ...

  4. 「模拟赛20190327」 第二题 DP+决策单调性优化

    题目描述 小火车虽然很穷,但是他还是得送礼物给妹子,所以他前往了二次元寻找不需要钱的礼物. 小火车准备玩玩二次元的游戏,游戏当然是在一个二维网格中展开的,网格大小是\(n\times m\)的,某些格 ...

  5. BZOJ4899: 记忆的轮廓【概率期望DP】【决策单调性优化DP】

    Description 通往贤者之塔的路上,有许多的危机. 我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增, 在[1,n]中,一共有n个节点.我 ...

  6. 决策单调性优化dp 专题练习

    决策单调性优化dp 专题练习 优化方法总结 一.斜率优化 对于形如 \(dp[i]=dp[j]+(i-j)*(i-j)\)类型的转移方程,维护一个上凸包或者下凸包,找到切点快速求解 技法: 1.单调队 ...

  7. Lightning Conductor 洛谷P3515 决策单调性优化DP

    遇见的第一道决策单调性优化DP,虽然看了题解,但是新技能√,很开森. 先%FlashHu大佬,反正我是看了他的题解和精美的配图才明白的,%%%巨佬. 废话不多说,看题: 题目大意 已知一个长度为n的序 ...

  8. CF868F Yet Another Minimization Problem 分治决策单调性优化DP

    题意: 给定一个序列,你要将其分为k段,总的代价为每段的权值之和,求最小代价. 定义一段序列的权值为$\sum_{i = 1}^{n}{\binom{cnt_{i}}{2}}$,其中$cnt_{i}$ ...

  9. 2018.10.14 NOIP训练 猜数游戏(决策单调性优化dp)

    传送门 一道神奇的dp题. 这题的决策单调性优化跟普通的不同. 首先发现这道题只跟r−lr-lr−l有关. 然后定义状态f[i][j]f[i][j]f[i][j]表示猜范围为[L,L+i−1][L,L ...

随机推荐

  1. Python特色数据类型--列表

    #list[起始索引:终止索引(不包含):步长间隔] list1[5:8] #步长省略则默认为1 #修改元素列表 #列表是一种可变的数据类型,所以可以修改内容 list1 = [0,1,2,3,4] ...

  2. 使用iview ui库 [vue/no-parsing-error] Parsing error: x-invalid-end-tag报错

    打开设置,搜索“vetur.validation.template”,设置完毕之后记得重启vscode 如果不行请使用下边方法 一. 问题日志 ✘ https://google.com/#q=vue% ...

  3. Tika检测文件类型

    Tika类型检测 Tika支持MIME所提供的所有互联网媒体文件类型.每当一个文件通过Tika检测到该文件,其文件类型.检测的介质类型,Tika内部通过以下机制. MIME标准 多用途Internet ...

  4. python之项目依赖管理

    生成所有依赖清单 requirements.txt 1.  pipreqs 工具 安装) pip install pipreqs 执行生成依赖列表命令) pipreqs ./ 完善版本: pipreq ...

  5. MYSQL 创建数据库以及表

    创建数据库,表 创建一个数据库,再在数据库下创建一个或多个表,不难,记不住的同学可以直接copy,慢慢的用会即刻,懂的同学请看代码,没有太多基础的同学,除了看代码,请看最下方的知识点 创建数据库: C ...

  6. Troubleshooting: Cannot Run on an Android Device

    同事在他的开发环境中,在IDE中直接在手机上运行Android项目,结果出现这个错误,无法在手机上安装. 产生这个问题的原因,一般就是签名不对,这种情况,删除手机上装过的同名应用,就可以解决.当然,你 ...

  7. Delphi TIdTCPClient组件

    樊伟胜

  8. python面向编程:类继承、继承案例、单继承下属性查找、super方法

    一.类的继承 二.基于继承解决类与类的代码冗余问题 三.在单继承背景下属性的查找 四.super的方法 一.类的继承 1.什么是继承? 在程序中继承是一种新建子类的方法的方式,新创建的类成为子类\派生 ...

  9. 从n个数里面找最大的两个数理论最少需要比较

    答案是:n+logn-2 过程是这样的: 甲乙比甲胜出,丙丁比丙胜出,最后甲丙比较,甲胜出...容易得出找出最大数为n-1次. 现在开始找出第二大的数字:明显,第二大的数字,一定和甲进行过比较.... ...

  10. 第1章 python入门

    1.1 python的出生与应用   python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆(中文名字:龟叔)为了在阿姆斯特丹打发时间,决心开 ...