记忆的轮廓

题目描述

通往贤者之塔的路上,有许多的危机。
我们可以把这个地形看做是一颗树,根节点编号为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的父亲。
50<=p<=n<=700,m<=1500,T<=5。
数据保证每个正确节点均有至少2个儿子,至多3个儿子。

输出格式

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

样例

样例输入

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

样例输出

  1. 9.0000
  2. 题解
  3. 考场上推出了一个死掉的弱智算法
    然而精度出了问题,加上本题根本没有小数据,被强行卡成了0
    我们不关注走到错误节点之后的去向
    只要dfs处理出 每个正确节点的错误节点儿子走出树的期望步数即可
    dpij)表示从第n个点,中间存了j个档,第j个存档在节点i的期望步数,
    使用高斯消元的思想,我们可以得到dpij)由dpkj-1)转移的公式 i<kn
    dfs打出来即可
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. const double inf=1e10;
  6. const int N=,M=;
  7. int n,m,p,tot;
  8. int first[M],to[M],nxt[M],cd[M];
  9. double w[M],dp[N][N];
  10. void dfs(int x)
  11. {
  12. if(x==n) return ;
  13. if(x<n) dfs(x+);
  14. w[x]=;
  15. for(int i=first[x];i;i=nxt[i])
  16. {
  17. dfs(to[i]);
  18. w[x]+=w[to[i]]/cd[x];
  19. }
  20. }
  21. void add(int a,int b)
  22. {
  23. cd[a]++;
  24. to[++tot]=b;
  25. nxt[tot]=first[a];
  26. first[a]=tot;
  27. }
  28. inline int read()
  29. {
  30. int x=; char ch=getchar();
  31. while(!isdigit(ch)) ch=getchar();
  32. while(isdigit(ch))
  33. {
  34. x=(x<<)+(x<<)+(ch^);
  35. ch=getchar();
  36. }
  37. return x;
  38. }
  39. int th;
  40. pair<double,double> search(int pos,int aim,int k)//系数 常数
  41. {
  42. pair<double,double> now,f;
  43. if(pos>n)
  44. {
  45. now.first=; now.second=w[pos];
  46. return now;
  47. }
  48. if(pos==aim)
  49. {
  50. now.first=; now.second=dp[aim][k]+;
  51. return now;
  52. }
  53. now.first=now.second=;
  54. f=search(pos+,aim,k);
  55. now.first+=f.first/cd[pos];
  56. now.second+=f.second/cd[pos];
  57. for(int i=first[pos];i;i=nxt[i])
  58. {
  59. f=search(to[i],aim,k);
  60. now.first+=f.first/cd[pos];
  61. now.second+=f.second/cd[pos];
  62. }
  63. if(pos!=th) now.second+=;
  64. return now;
  65. }
  66. void dpt(int pos,int k)//在pos处 第k个存档
  67. {
  68. th=pos;
  69. for(int i=pos+;i<=min(n,n-k+);i++)//由在i处 存k-1个档转移
  70. {
  71. pair<double,double> now=search(pos,i,k-);
  72. dp[pos][k]=min(dp[pos][k],now.second/(-now.first));
  73. }
  74. }
  75. int main()
  76. {
  77. int T; T=read();
  78. while(T--)
  79. {
  80. memset(first,,sizeof(first));
  81. memset(cd,,sizeof(cd));
  82. tot=;
  83. n=read(); m=read(); p=read();
  84. for(int i=;i<=n;i++)
  85. for(int j=;j<=n;j++)
  86. dp[i][j]=inf;
  87. for(int i=,f,t;i<=m-n;i++)
  88. {
  89. f=read(); t=read();
  90. add(f,t);
  91. }
  92. dfs();
  93. for(int i=;i<=m;i++) w[i]+=;
  94. for(int i=;i<=n;i++) cd[i]++;
  95. dp[n][]=;
  96. for(int i=n-;i>=;i--)
  97. for(int j=;j<=min(p,n-i+);j++)
  98. dpt(i,j);
  99. printf("%.4lf",dp[][p]);
  100. }
  101. return ;
  102. }
  103. /*
  104. 1
  105. 3 7 2
  106. 1 4
  107. 2 5
  108. 3 6
  109. 3 7
  110. */
  1.  

将重复的dfs删去,改成循环的形式,可以得到O(n2p)的算法

然而被卡了精度,只好改了longdouble

正解(或许)

四边形不等式:对于任意a<b<=c<d,

          转移代价w满足w(a,d)+w(b,c)<=w(a,c)+w(b,d)

对于形如    f[i]=max{f[i],f[j]+w(j,i)}    的转移方程

  满足决策单调性,即如果i的最优决策点在j,大于i的任何元素的最优决策点一定大于等于j

预处理出从存档点i到存档点j的cost数组

结合实际意义发现满足四边形不等式

于是可以用决策单调性

使「1,n」区间决策「1,n」区间

在向下递归过程中,扫一遍决策区间,找出 被决策区间中点 的最优决策点

中点左侧的区间,由最优决策点及左侧的区间决策

右侧的区间由右侧的区间决策

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. const int N=,M=;
  6. const double inf=1e99;
  7. int n,m,p,tot,first[M],to[M],nxt[M],cd[M];
  8. double w[M],dp[N][N],cost[N][N];
  9. void dfs(int x)
  10. {
  11. if(x==n) return ;
  12. if(x<n) dfs(x+);
  13. w[x]=;
  14. for(int i=first[x];i;i=nxt[i])
  15. {
  16. dfs(to[i]);
  17. w[x]+=w[to[i]]/cd[x];
  18. }
  19. }
  20. void add(int a,int b)
  21. {
  22. cd[a]++;
  23. to[++tot]=b;
  24. nxt[tot]=first[a];
  25. first[a]=tot;
  26. }
  27. inline int read()
  28. {
  29. int x=; char ch=getchar();
  30. while(!isdigit(ch)) ch=getchar();
  31. while(isdigit(ch))
  32. {
  33. x=(x<<)+(x<<)+(ch^);
  34. ch=getchar();
  35. }
  36. return x;
  37. }
  38. void solve(int dep,int l,int r,int lf,int rf)//lf rf决策l到r
  39. {
  40. if(l>r) return ;
  41. int mid=l+r>>,home=lf;
  42. dp[mid][dep]=inf;
  43. for(int i=lf;i<=min(rf,mid-);i++)
  44. {
  45. double tmp=dp[i][dep-]+cost[i][mid];
  46. if(tmp<dp[mid][dep]) dp[mid][dep]=tmp,home=i;
  47. }
  48. solve(dep,l,mid-,lf,home);
  49. solve(dep,mid+,r,home,rf);
  50. }
  51. int main()
  52. {
  53. int T; T=read();
  54. while(T--)
  55. {
  56. memset(first,,sizeof(first));
  57. memset(cd,,sizeof(cd));
  58. tot=;
  59. n=read(); m=read(); p=read();
  60. for(int i=,f,t;i<=m-n;i++)
  61. {
  62. f=read(); t=read();
  63. add(f,t);
  64. }
  65. dfs();
  66. for(int i=;i<=m;i++) w[i]+=;
  67. for(int i=;i<=n;i++) cd[i]++;
  68. for(int i=;i<=n;i++)
  69. {
  70. cost[i][i]=;
  71. for(int j=i+;j<=n;j++)
  72. {
  73. cost[i][j]=cd[j-]*cost[i][j-]+;
  74. for(int u=first[j-];u;u=nxt[u])
  75. cost[i][j]+=w[to[u]];
  76. }
  77. }
  78. for(int i=;i<=n;i++) dp[i][]=inf;
  79. dp[][]=;
  80. for(int i=;i<=p;i++)
  81. solve(i,,n,,n);
  82. printf("%.4lf\n",dp[n][p]);
  83. }
  84. return ;
  85. }

刚开始做的时候inf设的不够大,导致无法更新,也就无法固定决策的区间,出现了问题

记忆的轮廓 期望 四边形不等式dp|题解的更多相关文章

  1. 【整理】石子合并问题(四边形不等式DP优化)

    有很多种算法: 1,任意两堆可以合并:贪心+单调队列. 2,相邻两堆可合并:区间DP    (O(n^3)) ). 3,相邻,四边形不等式优化DP (O(n^2) ). 4,相邻,GarsiaWach ...

  2. [HDU3516] Tree Construction [四边形不等式dp]

    题面: 传送门 思路: 这道题有个结论: 把两棵树$\left[i,k\right]$以及$\left[k+1,j\right]$连接起来的最小花费是$x\left[k+1\right]-x\left ...

  3. [HDU3480] Division [四边形不等式dp]

    题面: 传送门 思路: 因为集合可以无序选择,所以我们先把输入数据排个序 然后发先可以动归一波 设$dp\left[i\right]\left[j\right]$表示前j个数中分了i个集合,$w\le ...

  4. [POJ1160] Post Office [四边形不等式dp]

    题面: 传送门 思路: dp方程实际上很好想 设$dp\left[i\right]\left[j\right]$表示前$j$个镇子设立$i$个邮局的最小花费 然后状态转移: $dp\left[i\ri ...

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

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

  6. [bzoj4899]记忆的轮廓 题解(毒瘤概率dp)

    题目背景 四次死亡轮回后,昴终于到达了贤者之塔,当代贤者夏乌拉一见到昴就上前抱住了昴“师傅!你终于回来了!你有着和师傅一样的魔女的余香,肯定是师傅”.众所周知,大贤者是嫉妒魔女沙提拉的老公,400年前 ...

  7. bzoj 4899 记忆的轮廓 题解(概率dp+决策单调性优化)

    题目背景 四次死亡轮回后,昴终于到达了贤者之塔,当代贤者夏乌拉一见到昴就上前抱住了昴“师傅!你终于回来了!你有着和师傅一样的魔女的余香,肯定是师傅”.众所周知,大贤者是嫉妒魔女沙提拉的老公,400年前 ...

  8. 区间dp+四边形不等式优化

    区间dp+四边形优化 luogu:p2858 题意 给出一列数 \(v_i\),每天只能取两端的数,第 j 天取数价值为\(v_i \times j\),最大价值?? 转移方程 dp[i][j] :n ...

  9. BZOJ1563/洛谷P1912 诗人小G 【四边形不等式优化dp】

    题目链接 洛谷P1912[原题,需输出方案] BZOJ1563[无SPJ,只需输出结果] 题解 四边形不等式 什么是四边形不等式? 一个定义域在整数上的函数\(val(i,j)\),满足对\(\for ...

随机推荐

  1. 快速学会使用Vuex

    一.Vuex简介 官方定义 Vuex是一个专门为Vue.js应用程序开的状态管理模式 它采用集中式存储管理应用的所有组件的状态 并以相应的规则保证以一种可预测的方式发生变化 二.应用场景 多个视图依赖 ...

  2. 错误:error: failed to push some refs to 'https://github.com/pzq7025/KG.git'的解决办法

    一.问题在进行[git push orgin master]的时候出现如下错误 ! [rejected] master -> master (non-fast-forward) error: f ...

  3. Java学习:Junit简介

    Junit简介 概述: JUnit 是用于编写和运行可重复的自动化测试的开源测试框架,这样可以保证我们的代码按预期工作.JUnit 可广泛用于工业和作为支架(从命令行)或IDE(如 IDEA)内单独的 ...

  4. golang学习笔记 ---rand

    在Golang中,有两个包提供了rand,分别为 "math/rand" 和 "crypto/rand",  对应两种应用场景. "math/rand ...

  5. 2019-11-29-WPF-绑定命令在-MVVM-的-CanExecute-和-Execute-在按钮点击都没触发可能的原因...

    原文:2019-11-29-WPF-绑定命令在-MVVM-的-CanExecute-和-Execute-在按钮点击都没触发可能的原因... title author date CreateTime c ...

  6. 轻量级ORM《sqlcommon》第一个版本发布了!!!

    一.sqlcommon的特色 1. 轻量级,整个包只有123kb. 2. 性能好,自测... 3. API和功能简单.代码简短.可维护性好基本都能看懂.这个点我认为很重要,你不用为了实现一个需求而四处 ...

  7. C#中的虚函数virtual

    简单介绍虚函数virtual 在某基类中声明 virtual 并在一个或多个派生类中被重新定义的成员函数称为虚函数. 虚函数的作用就是实现多态性(Polymorphism),多态性是将接口与实现进行分 ...

  8. TP5.1 调用common里面自定义的常量

    公共文件:\application\common.php define('cms_password', cms); 控制器引用: 调用: $aa = cms_password; dump(cms_pa ...

  9. 网站怎么上传到服务器流程,从本地到服务器上线过程并通过域名(IP地址)进行访问

    制作好的网页想要发布到互联网,该怎么发布呢?我们需要将保存在本地的站点上传站点到服务器,首先我们需要准备一个服务器(可通过服务器公网IP地址访问),也可以购买域名,域名购买可以通过阿里云.腾讯云.百度 ...

  10. HandBrake-QuickSync-Mac (内容:QuickSync encoder via VideoToolbox )

    来源:https://github.com/galad87/HandBrake-QuickSync-Mac/commit/2c1332958f7095c640cbcbcb45ffc955739d594 ...