记忆的轮廓

题目描述

通往贤者之塔的路上,有许多的危机。
我们可以把这个地形看做是一颗树,根节点编号为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|题解的更多相关文章

  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. POSIX 正则表达式 BRE与ERE的差异

    BRE,标准正则表达式,basic regular expressions ERE,扩展正则表达式,Extended Regular Expressions POSIX 正则表达式 传统上,POSIX ...

  2. How to get the free disk space in PostgreSQL (PostgreSQL获取磁盘空间)

    Get the current free disk space in PostgreSQL PostgreSQL获取磁盘空间 from eshizhan Here has a simple way t ...

  3. [转] linux 查找文本过滤grep正则表达式命令详解用法

    grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用 ...

  4. WPF 精修篇 动画组TransformGroup

    原文:WPF 精修篇 动画组TransformGroup 动画分组 TransformGroup 一个元素可能要有缩放 ScaleTransform和移动 TranslateTransform等多个效 ...

  5. .net core启用Swagger

    启用 Swagger 页面    官方文档推荐两种框架:Swashbuckle 和 NSwag,这里使用 Swashbuckle 来生成接口文档. 目录 安装包 添加服务 配置中间件 添加 UI 设置 ...

  6. SQLServer之Case用法

    定义 计算条件列表,并返回多个可能的结果表达式之一. 表达式类型 case具有两种格式: 简单case表达式,它通过将表达式与一组简单的表达式进行比较来确定结果. case搜索表达式,它通过计算一组布 ...

  7. Python关于多继承

    大部分面向对象的编程语言(除了C++)都只支持单继承,而不支持多继承,为什么呢?因为多继承不仅增加编程复杂度,而且容易导致莫名其妙的错误. Python虽然语法上支持多继承,但是却不推荐使用多继承,而 ...

  8. Redis主从复制看这篇就够了

    一.概念和作用 概念:主机数据更新后根据配置和策略, 自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主. 作用: 读写分离: 容灾恢复: 二.复制原理 slav ...

  9. 北理工机器人队RM视觉组ubuntu必备软件安装指南

    基础软件安装 sudo apt update && sudo apt upgrade sudo apt install git curl vim gcc g++ cmake与make ...

  10. v-bind 属性绑定

    1.v-bind:title="title" 绑定谁和谁绑定. 2.v-bind:title="title" 简写::title="title&quo ...