Bzoj4899 记忆的轮廓
B. 记忆的轮廓
题目描述
通往贤者之塔的路上,有许多的危机。
我们可以把这个地形看做是一颗树,根节点编号为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
orz大佬题解:https://blog.csdn.net/WerKeyTom_FTD/article/details/53026266
我这里只是梳理一下自己的思路。
50算法
对于n==p的情况,就是在每一个点都存档,设d[i]表示节点i的儿子数,对于错误节点i,设g[i]为读档的期望步数,则g[i]=1+∑(1/d[i]*g[j]).对于正确节点i,设s[i]=∑g[j](j为i的错误儿子)。设f[i]为从i到n的期望步数,f[n]=0,f[i]=1+f[i+1]/d[i]+∑(g[j]+f[i])/d[i],得f[i]=d[i]+f[i+1]+s[i];
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
struct edge
{
int u,v,next;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].next
}ed[];
int first[],num_e;
#define f(x) first[x]
int T;
int n,m,p;
double d[],g[],s[],f[]; void dfs(int x,int fa)
{
//cout<<x<<" "<<fa<<endl;
if(x>n)g[x]=; for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
d[x]++; for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
dfs(v(i),x);
if(x>n)g[x]+=/d[x]*g[v(i)];
else s[x]+=g[v(i)];
}
if(x<n) f[x]=d[x]+s[x]+f[x+];
}
inline void add(int u,int v);
signed main()
{
cin>>T;
while(T--)
{
cin>>n>>m>>p;
for(int i=;i<n;i++)
add(i,i+),add(i+,i);
int u,v;
for(int i=;i<=m-n;i++)
{
cin>>u>>v;
add(u,v),add(v,u);
}
if(n==p)
{
dfs(,);
printf("%0.4lf\n",f[]);
}
} }
inline void add(int u,int v)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
n(num_e)=f(u);
f(u)=num_e;
}
70算法
设a[i][j]为当前存档点为i,从i出发到j的期望步数,用n2的复杂度预处理出a,a[i][i]=0,对于i<j,a[i][j]=a[i][j-1]+1+1/d[j-1]*0+∑(g[k]+a[i][j])/d[j-1];(k为j-1的错误儿子),得a[i][j]=a[i][j-1]*d[j-1]+d[j-1]+s[j-1];这里理解了好长时间,我本来写的是a[i][j]=a[i][j-1]+1/d[j-1]+∑(g[k]+a[i][j])/d[j-1];因为从j-1无论如何都要再走一步到他的儿子节点,我是忘了这个……设f[i][j]表示当前存档点为i,还剩j次存档机会,到n的期望步数,
那么f[i][j]=min(f[k][j-1]+a[i][k]),i<k<=n;最后答案为f[1][p-1];复杂度O(n2p);
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define ma(x) memset(x,0,sizeof(x))
using namespace std;
struct edge
{
int u,v,next;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].next
}ed[];
int first[],num_e;
#define f(x) first[x]
int T;
int n,m,p;
double d[],g[],s[],f[]; double a[][],f2[][]; void dfs(int x,int fa);
inline void add(int u,int v);
signed main()
{
//freopen("5.in","r",stdin); cin>>T;
while(T--)
{
num_e=;ma(d);ma(g);ma(s);ma(f);ma(f2);ma(a);ma(first);
cin>>n>>m>>p;
for(int i=;i<n;i++)
add(i,i+),add(i+,i);
int u,v;
for(int i=;i<=m-n;i++)
{
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs(,);
if(n==p)
{
printf("%0.4lf\n",f[]);
continue;
}
for(int i=;i<=n;i++)
for(int j=i+;j<=n;j++)
a[i][j]=a[i][j-]*d[j-]+d[j-]+s[j-];
for(int i=n-;i;i--)
for(int j=;j<=p;j++)
f2[i][j]=0x7fffffff;
for(int i=n;i>=;i--)
for(int j=;j<p;j++)
for(int k=i+;k<=n;k++)
f2[i][j]=min(f2[i][j] , f2[k][j-]+a[i][k] );
printf("%0.4lf\n",f2[][p-]);
} }
inline void add(int u,int v)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
n(num_e)=f(u);
f(u)=num_e;
}
void dfs(int x,int fa)
{
if(x>n)g[x]=;
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
d[x]++;
//cout<<i<<" "<<v(i)<<endl;
}
//cout<<x<<endl;
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
dfs(v(i),x);
if(x>n)g[x]+=/d[x]*g[v(i)];
else s[x]+=g[v(i)];
}
if(x<n) f[x]=d[x]+s[x]+f[x+];
}
70算法+玄学优化AC
题解的分析没有看懂,就是证明了一下a数组不会爆炸的问题,a的增长是非常快的,但答案并不会有那么大,所以可以假定一个常数step,每次转移最多从距离step转移过来,step取40就差不多了,因为a的下界是2^40了,而答案的上界远远没有达到。题解说复杂度是O(np log ans),没有搞懂,O(40np)更容易理解吧,其实也差不多。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define ma(x) memset(x,0,sizeof(x))
using namespace std;
struct edge
{
int u,v,next;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].next
}ed[];
int first[],num_e;
#define f(x) first[x]
int T;
int n,m,p;
double d[],g[],s[],f[]; double a[][],f2[][];
const int step=; void dfs(int x,int fa);
inline void add(int u,int v);
signed main()
{
//freopen("5.in","r",stdin); cin>>T;
while(T--)
{
num_e=;ma(d);ma(g);ma(s);ma(f);ma(f2);ma(a);ma(first);
cin>>n>>m>>p;
for(int i=;i<n;i++)
add(i,i+),add(i+,i);
int u,v;
for(int i=;i<=m-n;i++)
{
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs(,);
if(n==p)
{
printf("%0.4lf\n",f[]);
continue;
}
for(int i=;i<=n;i++)
for(int j=i+;j<=n;j++)
a[i][j]=a[i][j-]*d[j-]+d[j-]+s[j-];
for(int i=n-;i;i--)
for(int j=;j<=p;j++)
f2[i][j]=0x7fffffff;
for(int i=n;i>=;i--)
for(int j=;j<p;j++)
for(int k=i+;k<=min(i+step,n);k++)
f2[i][j]=min(f2[i][j] , f2[k][j-]+a[i][k] );
printf("%0.4lf\n",f2[][p-]);
} }
inline void add(int u,int v)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
n(num_e)=f(u);
f(u)=num_e;
}
void dfs(int x,int fa)
{
if(x>n)g[x]=;
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
d[x]++;
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
dfs(v(i),x);
if(x>n)g[x]+=/d[x]*g[v(i)];
else s[x]+=g[v(i)];
}
if(x<n) f[x]=d[x]+s[x]+f[x+];
}
100算法
居然是个单队。首先证明a[i][j+1]-a[i][j]>=a[i+1][j+1]-a[i+1][j],把a[i][j+1]展开,得左边=(d[j]-1)*a[i][j]+…,右边=(d[j]-1)*a[i+1][j]+…。得证。则可以用单队优化,复杂度O(np log n)。
另外还有一种做法,目前还没有看懂……
Bzoj4899 记忆的轮廓的更多相关文章
- [BZOJ4899]:记忆的轮廓(概率DP)
题目传送门 题目描述: 通往贤者之塔的路上,有许多的危机. 我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增, 在[1,n]中,一共有n个节点.我 ...
- [bzoj4899]记忆的轮廓 题解(毒瘤概率dp)
题目背景 四次死亡轮回后,昴终于到达了贤者之塔,当代贤者夏乌拉一见到昴就上前抱住了昴“师傅!你终于回来了!你有着和师傅一样的魔女的余香,肯定是师傅”.众所周知,大贤者是嫉妒魔女沙提拉的老公,400年前 ...
- BZOJ4899 记忆的轮廓(概率期望+动态规划+决策单调性)
容易发现跟树没什么关系,可以预处理出每个点若走向分叉点期望走多少步才能回到上个存档点,就变为链上问题了.考虑dp,显然有f[i][j]表示在i~n中设置了j个存档点,其中i设置存档点的最优期望步数.转 ...
- BZOJ4899: 记忆的轮廓【概率期望DP】【决策单调性优化DP】
Description 通往贤者之塔的路上,有许多的危机. 我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增, 在[1,n]中,一共有n个节点.我 ...
- 记忆的轮廓 期望 四边形不等式dp|题解
记忆的轮廓 题目描述 通往贤者之塔的路上,有许多的危机.我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增,在[1,n]中,一共有n个节点.我们把编 ...
- bzoj 4899 记忆的轮廓 题解(概率dp+决策单调性优化)
题目背景 四次死亡轮回后,昴终于到达了贤者之塔,当代贤者夏乌拉一见到昴就上前抱住了昴“师傅!你终于回来了!你有着和师傅一样的魔女的余香,肯定是师傅”.众所周知,大贤者是嫉妒魔女沙提拉的老公,400年前 ...
- loj6171/bzoj4899 记忆的轮廊(期望dp+优化)
题目: https://loj.ac/problem/6171 分析: 设dp[i][j]表示从第i个点出发(正确节点),还可以有j个存档点(在i点使用一个存档机会),走到终点n的期望步数 那么 a[ ...
- BZOJ 4899 记忆的轮廓
话说BZOJ 是不是死了啊 (已经没有传送门了) 设 $f[i][j]$ 表示走到第 $j$ 个位置确定了 $i$ 个存档点时的最小代价,并强制第 $j$ 个位置有一个存档点 那么设 $cst[i][ ...
- NOIP模拟 1
NOIP模拟1,到现在时间已经比较长了.. 那天是6.14,今天7.18了 //然鹅我看着最前边缺失的模拟1,还是终于忍不住把它补上,为了保持顺序2345重新发布了一遍.. # 用 户 名 ...
随机推荐
- [Django基础] django解决静态文件依赖问题以及前端引入方式
一.静态文件依赖 学习django的时候发现静态文件(css,js等)不能只在html中引入,还要在项目的settings中设置,否则会报以下错误 [11/Sep/2018 03:18:15] &qu ...
- 洛谷 P1313 计算系数 —— 水题
题目:https://www.luogu.org/problemnew/show/P1313 不就是...C(k,n) * an * bm . 代码如下: #include<iostream&g ...
- python解决list unicode转中文显示
#!/usr/bin/python# #-*-coding:UTF-8-*- import xlrd book = xlrd.open_workbook('Interface_data.xlsx') ...
- C++实现用两个栈实现队列
/* * 用两个栈实现队列.cpp * * Created on: 2018年4月7日 * Author: soyo */ #include<iostream> #include<s ...
- linux设备驱动第三篇:如何写一个简单的字符设备驱动?
在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存 ...
- (5)css盒子模型(基础上)
CSS 盒子模型概述 ***什么是CSS的盒子模型呢?为什么叫它是盒子?先说说我们在网页设计中常听的属性名:内容(content).边框(border).内边距(padding).外边距(margin ...
- 标准行cp功能
#include<stdio.h> int main(int argc,char *argv[]) { FILE *src_fp,*des_fp; ]={}; ) { printf(&qu ...
- POJ 3683 Priest John's Busiest Day
看这个题目之前可以先看POJ2186复习一下强联通分量的分解 题意:给出N个开始时间和结束时间和持续时间三元组,持续时间可以在开始后或者结束前,问如何分配可以没有冲突. -----–我是分割线---- ...
- 018 [工具软件]截图贴图注释 Snipaste
Snipaste 是一个截图贴图工具,绿色免费.官方主页:https://zh.snipaste.com/. 三大功能: 1.截图,可以自动识别窗口的各元素,可以精准到像素调整截图区域大小. 2.贴图 ...
- POJ 2002 Squares 数学 + 必须hash
http://poj.org/problem?id=2002 只能说hash比二分快很多.随便一个hash函数都可以完爆二分. 判断是否存在正方形思路如下: 1.枚举任意两个点,作为正方形的一条边,那 ...