记忆的轮廓 期望 四边形不等式dp|题解
记忆的轮廓
题目描述
通往贤者之塔的路上,有许多的危机。
我们可以把这个地形看做是一颗树,根节点编号为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
3 7 2
1 4
2 5
3 6
3 7
样例输出
9.0000 题解 考场上推出了一个死掉的弱智算法
然而精度出了问题,加上本题根本没有小数据,被强行卡成了0分
我们不关注走到错误节点之后的去向
只要dfs处理出 每个正确节点的错误节点儿子走出树的期望步数即可
设dp(i,j)表示从第n个点,中间存了j个档,第j个存档在节点i的期望步数,
使用高斯消元的思想,我们可以得到dp(i,j)由dp(k,j-1)转移的公式 (i<k≤n)dfs打出来即可
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const double inf=1e10;
const int N=,M=;
int n,m,p,tot;
int first[M],to[M],nxt[M],cd[M];
double w[M],dp[N][N];
void dfs(int x)
{
if(x==n) return ;
if(x<n) dfs(x+);
w[x]=;
for(int i=first[x];i;i=nxt[i])
{
dfs(to[i]);
w[x]+=w[to[i]]/cd[x];
}
}
void add(int a,int b)
{
cd[a]++;
to[++tot]=b;
nxt[tot]=first[a];
first[a]=tot;
}
inline int read()
{
int x=; char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch))
{
x=(x<<)+(x<<)+(ch^);
ch=getchar();
}
return x;
}
int th;
pair<double,double> search(int pos,int aim,int k)//系数 常数
{
pair<double,double> now,f;
if(pos>n)
{
now.first=; now.second=w[pos];
return now;
}
if(pos==aim)
{
now.first=; now.second=dp[aim][k]+;
return now;
}
now.first=now.second=;
f=search(pos+,aim,k);
now.first+=f.first/cd[pos];
now.second+=f.second/cd[pos];
for(int i=first[pos];i;i=nxt[i])
{
f=search(to[i],aim,k);
now.first+=f.first/cd[pos];
now.second+=f.second/cd[pos];
}
if(pos!=th) now.second+=;
return now;
}
void dpt(int pos,int k)//在pos处 第k个存档
{
th=pos;
for(int i=pos+;i<=min(n,n-k+);i++)//由在i处 存k-1个档转移
{
pair<double,double> now=search(pos,i,k-);
dp[pos][k]=min(dp[pos][k],now.second/(-now.first));
}
}
int main()
{
int T; T=read();
while(T--)
{
memset(first,,sizeof(first));
memset(cd,,sizeof(cd));
tot=;
n=read(); m=read(); p=read();
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
dp[i][j]=inf;
for(int i=,f,t;i<=m-n;i++)
{
f=read(); t=read();
add(f,t);
}
dfs();
for(int i=;i<=m;i++) w[i]+=;
for(int i=;i<=n;i++) cd[i]++;
dp[n][]=;
for(int i=n-;i>=;i--)
for(int j=;j<=min(p,n-i+);j++)
dpt(i,j);
printf("%.4lf",dp[][p]);
}
return ;
}
/*
1
3 7 2
1 4
2 5
3 6
3 7
*/
将重复的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」区间
在向下递归过程中,扫一遍决策区间,找出 被决策区间中点 的最优决策点
中点左侧的区间,由最优决策点及左侧的区间决策
右侧的区间由右侧的区间决策
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=,M=;
const double inf=1e99;
int n,m,p,tot,first[M],to[M],nxt[M],cd[M];
double w[M],dp[N][N],cost[N][N];
void dfs(int x)
{
if(x==n) return ;
if(x<n) dfs(x+);
w[x]=;
for(int i=first[x];i;i=nxt[i])
{
dfs(to[i]);
w[x]+=w[to[i]]/cd[x];
}
}
void add(int a,int b)
{
cd[a]++;
to[++tot]=b;
nxt[tot]=first[a];
first[a]=tot;
}
inline int read()
{
int x=; char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch))
{
x=(x<<)+(x<<)+(ch^);
ch=getchar();
}
return x;
}
void solve(int dep,int l,int r,int lf,int rf)//lf rf决策l到r
{
if(l>r) return ;
int mid=l+r>>,home=lf;
dp[mid][dep]=inf;
for(int i=lf;i<=min(rf,mid-);i++)
{
double tmp=dp[i][dep-]+cost[i][mid];
if(tmp<dp[mid][dep]) dp[mid][dep]=tmp,home=i;
}
solve(dep,l,mid-,lf,home);
solve(dep,mid+,r,home,rf);
}
int main()
{
int T; T=read();
while(T--)
{
memset(first,,sizeof(first));
memset(cd,,sizeof(cd));
tot=;
n=read(); m=read(); p=read();
for(int i=,f,t;i<=m-n;i++)
{
f=read(); t=read();
add(f,t);
}
dfs();
for(int i=;i<=m;i++) w[i]+=;
for(int i=;i<=n;i++) cd[i]++;
for(int i=;i<=n;i++)
{
cost[i][i]=;
for(int j=i+;j<=n;j++)
{
cost[i][j]=cd[j-]*cost[i][j-]+;
for(int u=first[j-];u;u=nxt[u])
cost[i][j]+=w[to[u]];
}
}
for(int i=;i<=n;i++) dp[i][]=inf;
dp[][]=;
for(int i=;i<=p;i++)
solve(i,,n,,n);
printf("%.4lf\n",dp[n][p]);
}
return ;
}
刚开始做的时候inf设的不够大,导致无法更新,也就无法固定决策的区间,出现了问题
记忆的轮廓 期望 四边形不等式dp|题解的更多相关文章
- 【整理】石子合并问题(四边形不等式DP优化)
有很多种算法: 1,任意两堆可以合并:贪心+单调队列. 2,相邻两堆可合并:区间DP (O(n^3)) ). 3,相邻,四边形不等式优化DP (O(n^2) ). 4,相邻,GarsiaWach ...
- [HDU3516] Tree Construction [四边形不等式dp]
题面: 传送门 思路: 这道题有个结论: 把两棵树$\left[i,k\right]$以及$\left[k+1,j\right]$连接起来的最小花费是$x\left[k+1\right]-x\left ...
- [HDU3480] Division [四边形不等式dp]
题面: 传送门 思路: 因为集合可以无序选择,所以我们先把输入数据排个序 然后发先可以动归一波 设$dp\left[i\right]\left[j\right]$表示前j个数中分了i个集合,$w\le ...
- [POJ1160] Post Office [四边形不等式dp]
题面: 传送门 思路: dp方程实际上很好想 设$dp\left[i\right]\left[j\right]$表示前$j$个镇子设立$i$个邮局的最小花费 然后状态转移: $dp\left[i\ri ...
- [BZOJ4899]:记忆的轮廓(概率DP)
题目传送门 题目描述: 通往贤者之塔的路上,有许多的危机. 我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增, 在[1,n]中,一共有n个节点.我 ...
- [bzoj4899]记忆的轮廓 题解(毒瘤概率dp)
题目背景 四次死亡轮回后,昴终于到达了贤者之塔,当代贤者夏乌拉一见到昴就上前抱住了昴“师傅!你终于回来了!你有着和师傅一样的魔女的余香,肯定是师傅”.众所周知,大贤者是嫉妒魔女沙提拉的老公,400年前 ...
- bzoj 4899 记忆的轮廓 题解(概率dp+决策单调性优化)
题目背景 四次死亡轮回后,昴终于到达了贤者之塔,当代贤者夏乌拉一见到昴就上前抱住了昴“师傅!你终于回来了!你有着和师傅一样的魔女的余香,肯定是师傅”.众所周知,大贤者是嫉妒魔女沙提拉的老公,400年前 ...
- 区间dp+四边形不等式优化
区间dp+四边形优化 luogu:p2858 题意 给出一列数 \(v_i\),每天只能取两端的数,第 j 天取数价值为\(v_i \times j\),最大价值?? 转移方程 dp[i][j] :n ...
- BZOJ1563/洛谷P1912 诗人小G 【四边形不等式优化dp】
题目链接 洛谷P1912[原题,需输出方案] BZOJ1563[无SPJ,只需输出结果] 题解 四边形不等式 什么是四边形不等式? 一个定义域在整数上的函数\(val(i,j)\),满足对\(\for ...
随机推荐
- Prometheus 告警状态了解
Prometheus 告警状态了解 一旦这些警报存储在Alertmanager,它们可能处于以下任何状态: · Inactive:这里什么都没有发生. · Pending:已触发阈值,但未满足告警持续 ...
- 一、hexo+github搭建个人博客的过程记录
前提: 1.新建一个github仓库 2.安装配置Node.js 3.安装配置Git 前提 步骤1.新建一个github仓库 打开github网站,(注册)登录账号,新建一个仓库; 注:==仓库名称要 ...
- python排序 基数排序
算法思想 基数排序通过按位比较(一般从最低位开始)将元素按照最低位的数放到10个桶中,当所有的元素都这样被处理一次后,在按从0到9的顺序将每个桶的元素再取出来(不关注其他位的,只关注当前位的)这样就完 ...
- 一文读懂Java线程状态转换
前言 本文描述Java线程线程状态及状态转换,不会涉及过多理论,主要以代码示例说明线程状态如何转换. 基础知识 1. 线程状态 Thread源码中的状态说明: 线程可以有6种状态: New(新建) R ...
- 浅谈Spring中JDK动态代理与CGLIB动态代理
前言Spring是Java程序员基本不可能绕开的一个框架,它的核心思想是IOC(控制反转)和AOP(面向切面编程).在Spring中这两个核心思想都是基于设计模式实现的,IOC思想的实现基于工厂模式, ...
- English--倒装句
English|倒装句 这一块主要进行英语中倒装句与强调句的透析,希望大家可以掌握倒装句.因为倒装句,实在是太常见了,加油哦~~ 前言 目前所有的文章思想格式都是:知识+情感. 知识:对于所有的知识点 ...
- Windows+VS2017使用gRPC
gRPC官方Windows安装说明: Install Visual Studio 2015 or 2017 (Visual C++ compiler will be used). Install Gi ...
- iOS开发使用Xcode的一些小技巧
1.打开iOS项目 如果你当前目录下既有project又有workspace,你可以在终端使用命令“xed.”自动打开workspace,如果只有project,它会自动打开project. 2.清理 ...
- Jenkins+GitLab+Docker+SpringCloud实现可持续自动化微服务
本文很长很长,但是句句干货,点赞关注收藏后有惊喜在文末等你 现有混合云平台的场景下,即有线下和线上的环境,又有测试与正式的场景,而且结合了Docker,导致打包内容有所区分,且服务的发布流程复杂起来, ...
- HBase安装指南
一.事前准备 此安装是建立在hadoop集群运行起来的基础上,此hadoop版本为2.6.0,其他版本未测试,可能存在兼容性问题. 上传所需文件到/usr/local/soft 二.zookeep ...