之前做的一些图上的期望步数的题大多用到高斯消元来求解(HNOI游走,SDOI走迷宫,etc),因此我一开始做这道题的时候想偏了…

这道题的性质:聪聪和可可之间的最短路长度严格递减.因为聪聪总可以多走一步,那么变化有三种情况:最短路长度-1,-2,-3

于是我们发现,聪聪和可可所处的不同的状态之间是有序的,这让这道题与其他的图上期望步数题不同.例如”游走”中可以走到1再走到2再走到1….,走到1和走到2的状态之间没有先后顺序.但这个题中永远是聪聪可可之间距离大的状态转移到聪聪可可之间距离短的状态.

然后,正常做法是:记f[i][j]为聪聪在i,可可在j时追上可可的期望步数,最短路/BFS预处理g[i][j]为聪聪在i可可在j时聪聪走一步到达的点,记忆化搜索一发,又好写又短.

然而我脑残…直接定义p[i][j]为聪聪在i,可可在j这个状态在追及过程中出现过的概率,e[i][j]为到达聪聪在i,可可在j这个状态时的期望步数。我算的是从起点到某个状态的期望和概率,而不是正常的”某个状态到终点的期望”。

于是我们可以先BFS一遍所有的(i,j)状态组成的状态转移图,求出所有概率,然后按照BFS序在状态转移图上扫一遍求出期望.

注意BFS时要保证按照聪聪可可之间的距离递减的顺序求解,观察到,所有通过”最短路长度-1”方式转移得到的状态之间满足最短路长度递减, 所有通过”最短路长度-2”方式转移得到的状态之间也满足最短路长度递减,“最短路长度-3”同理(noip2016 D2T2的正解思路…现在用到也是悲伤…)所以我们开三个队列,就可以满足这个条件.

另一种可能的写法:把n^2个状态按照聪聪可可之间的距离排序,对应到一维的编号上然后按这个顺序DP?懒得写了…

(2017.1.4update:这么DP过不了,无用状态太多,常数太大...比较好的做法http://www.cnblogs.com/liu-runda/p/6250072.html)

以下是口胡:

边权是1我为什么要写dijkstra…

为什么我想不到把状态定义成从这个状态转移到终点的期望…

此外,为啥我连BFS节点入队之前要判重都不会了……

脑残怎么治啊…

#include<cstdio>
#include<queue>
using namespace std;
const int maxn=;
int n,m,s,t;
double f[maxn][maxn];
int g[maxn][maxn];
struct edge{
int to,next;
}lst[maxn<<];int len=,first[maxn];
void addedge(int a,int b){
lst[len].to=b;lst[len].next=first[a];
first[a]=len++;
}
int deg[maxn];
struct node{
int v,d;
node(int _v,int _d){
v=_v;d=_d;
}
bool operator <(const node &B)const{
return d>B.d;
}
};
bool vis[maxn][maxn];
int dis[maxn][maxn];
void dijkstra(int s,bool vis[],int dis[]){
priority_queue<node> q;
q.push(node(s,));
while(!q.empty()){
node tmp=q.top();q.pop();
if(vis[tmp.v])continue;
vis[tmp.v]=true;dis[tmp.v]=tmp.d;
for(int pt=first[tmp.v];pt;pt=lst[pt].next){
if(!vis[lst[pt].to])q.push(node(lst[pt].to,tmp.d+));
}
}
for(int i=;i<=n;++i){
g[i][s]=i;
for(int pt=first[i];pt;pt=lst[pt].next){
if(dis[lst[pt].to]<dis[g[i][s]])g[i][s]=lst[pt].to;
if(dis[lst[pt].to]==dis[g[i][s]]&&lst[pt].to<g[i][s])g[i][s]=lst[pt].to;
}
}
}
bool ok[maxn][maxn];
double dfs(int s,int t){
if(ok[s][t])return f[s][t];
if(s==t){
ok[s][t]=true;
return f[s][t]=;
}
int _s=g[g[s][t]][t];
if(_s==t){
ok[s][t]=true;
return f[s][t]=;
}
for(int pt=first[t];pt;pt=lst[pt].next){
f[s][t]+=dfs(_s,lst[pt].to)/deg[t];
}
ok[s][t]=true;
return f[s][t]+=;
}
int main(){
scanf("%d%d%d%d",&n,&m,&s,&t);
int a,b;
for(int i=;i<=m;++i){
scanf("%d%d",&a,&b);
addedge(a,b);addedge(b,a);
deg[a]++;deg[b]++;
}
for(int i=;i<=n;++i){
addedge(i,i);deg[i]++;
}
for(int i=;i<=n;++i){
dijkstra(i,vis[i],dis[i]);
}
printf("%.3f",dfs(s,t));
return ;
}
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=;
struct edge{
int to,next;
}lst[maxn<<];int len=,first[maxn];
void addedge(int a,int b){
lst[len].to=b;lst[len].next=first[a];
first[a]=len++;
}
double p[maxn][maxn],e[maxn][maxn];
int g[maxn][maxn],dis[maxn][maxn];
int n,m,s0,t0;
struct node{
int v,d;
node(int _v,int _d){v=_v;d=_d;}
bool operator <(const node &B)const{
return d>B.d;
}
};
int vis[maxn];
int T;
void dijkstra(int s,int dis[]){
++T;
priority_queue<node> q;
q.push(node(s,));
while(!q.empty()){
node tmp=q.top();q.pop();
if(vis[tmp.v]==T)continue;
vis[tmp.v]=T;dis[tmp.v]=tmp.d;
for(int pt=first[tmp.v];pt;pt=lst[pt].next){
if(vis[lst[pt].to]!=T)q.push(node(lst[pt].to,tmp.d+));
}
}
int ans;
for(int i=;i<=n;++i){
ans=i;
for(int pt=first[i];pt;pt=lst[pt].next){
if(dis[lst[pt].to]<dis[ans])ans=lst[pt].to;
if(dis[lst[pt].to]==dis[ans]&&lst[pt].to<ans)ans=lst[pt].to;
}
g[i][s]=ans;
}
}
int deg[maxn];
struct Node{
int s,t;
Node(int _s,int _t){s=_s;t=_t;}
Node(){};
}q[][maxn*maxn];//q[0]:-1 q[1]:-2 q[2]:-3
int head[],tail[];
bool used[maxn][maxn];
void bfs(){
while((head[]!=tail[])||(head[]!=tail[])||(head[]!=tail[])){
// getchar();printf("%d %d\n",head[0],tail[0]);
int tmp=-,Max=-;
for(int i=;i<;++i){
if(head[i]!=tail[i]&&dis[q[i][head[i]].s][q[i][head[i]].t]>Max){
tmp=i;Max=dis[q[i][head[i]].s][q[i][head[i]].t];
}
}
Node x=q[tmp][head[tmp]++];
if(x.s==x.t)continue;
int s1=g[g[x.s][x.t]][x.t];
if(s1==x.t){
p[s1][x.t]+=p[x.s][x.t];
}else{
for(int pt=first[x.t];pt;pt=lst[pt].next){
p[s1][lst[pt].to]+=p[x.s][x.t]/deg[x.t];
if(!used[s1][lst[pt].to]){
used[s1][lst[pt].to]=true;
if(dis[s1][lst[pt].to]==dis[x.s][x.t]-)q[][tail[]++]=Node(s1,lst[pt].to);
else if(dis[s1][lst[pt].to]==dis[x.s][x.t]-)q[][tail[]++]=Node(s1,lst[pt].to);
else q[][tail[]++]=Node(s1,lst[pt].to);
}
}
// p[s1][x.t]+=p[x.s][x.t]/deg[x.t];
// if(q[1][tail[1]++]=Node(s1,x.t);
} }
}
void cal(){
int h[];h[]=h[]=h[]=;
while(h[]!=tail[]||h[]!=tail[]||h[]!=tail[]){//printf("!");
int tmp=-,Max=-;
for(int i=;i<;++i){
if(h[i]!=tail[i]&&dis[q[i][h[i]].s][q[i][h[i]].t]>=Max){
tmp=i;Max=dis[q[i][h[i]].s][q[i][h[i]].t];
}
}
Node x=q[tmp][h[tmp]++];
if(x.s==x.t)continue;
int s1=g[g[x.s][x.t]][x.t];
if(s1==x.t){
e[x.t][x.t]+=(+e[x.s][x.t])*p[x.s][x.t]/p[x.t][x.t];
}else{
for(int pt=first[x.t];pt;pt=lst[pt].next){
e[s1][lst[pt].to]+=(+e[x.s][x.t])*p[x.s][x.t]/deg[x.t]/p[s1][lst[pt].to];
}
// e[s1][x.t]+=(1+e[x.s][x.t])*p[x.s][x.t]/deg[x.t]/p[s1][x.t];
}
}
}
int main(){
scanf("%d%d",&n,&m);
scanf("%d%d",&s0,&t0);
int a,b;
for(int i=;i<=m;++i){
scanf("%d%d",&a,&b);deg[a]++;deg[b]++;
addedge(a,b);addedge(b,a);
}
for(int i=;i<=n;++i){
deg[i]++;addedge(i,i);
dijkstra(i,dis[i]);
}
p[s0][t0]=1.0;e[s0][t0]=;
q[][tail[]++]=Node(s0,t0);
bfs();//get possibility
cal();//get expectations
double ans=;
for(int i=;i<=n;++i){
ans+=p[i][i]*e[i][i];
}
printf("%.3f\n",ans);
return ;
}

bzoj1415[NOI2005]聪聪和可可的更多相关文章

  1. 【BZOJ1415】【NOI2005】聪聪和可可(动态规划,数学期望)

    [BZOJ1415][NOI2005]聪聪和可可(动态规划,数学期望) 题面 BZOJ 题解 先预处理出当可可在某个点,聪聪在某个点时 聪聪会往哪里走 然后记忆化搜索一下就好了 #include< ...

  2. 【bzoj1415】 Noi2005—聪聪和可可

    http://www.lydsy.com/JudgeOnline/problem.php?id=1415 (题目链接) 题意 一张图,聪聪想吃可可.每单位时间聪聪可以先移动两次:可可后移动一次或停在原 ...

  3. 【BZOJ1415】 [Noi2005]聪聪和可可 概率与期望

    其实题不难,不知提交了几次...不能代码MD...注意一些基本问题...SB概率题 #include <iostream> #include <cstdio> #include ...

  4. BZOJ1415[Noi2005]聪聪和可可——记忆化搜索+期望dp

    题目描述 输入 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下来E行 ...

  5. 【bzoj1415】[Noi2005]聪聪和可可 期望记忆化搜索

    题目描述 输入 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下来E行 ...

  6. BZOJ1415 [Noi2005]聪聪和可可 【SPFA + 期望dp记忆化搜索】

    题目 输入格式 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下来E行 ...

  7. bzoj1415 [Noi2005]聪聪和可可【概率dp 数学期望】

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1415 noip2016 D1T3,多么痛的领悟...看来要恶补一下与期望相关的东西了. 这是 ...

  8. [BZOJ1415][NOI2005]聪聪与可可

    Description Input 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点 ...

  9. BZOJ 1415: [Noi2005]聪聪和可可( 最短路 + 期望dp )

    用最短路暴力搞出s(i, j)表示聪聪在i, 可可在j处时聪聪会走的路线. 然后就可以dp了, dp(i, j) = [ dp(s(s(i,j), j), j) + Σdp(s(s(i,j), j), ...

随机推荐

  1. MYSQL数据库导入出错:#1046 - No database selected

    今天遇到的mysql导入Navivat for MySql,总是出错,搞了一会才记起没有创建同名的数据库,然后还是导不进去,原来是要在建立的同名的数据单击右键---->运行Sql文件--> ...

  2. 关于mysql字段时间类型timestamp默认值为当前时间问题

    今天把应用部署到AWS上发现后台修改内容提交后程序报错,经过排查发现是更新数据的时候,有张数据表中的一个timestamp类型的字段默认值变成了"0000-00-00 00:00:00.00 ...

  3. Android开发学习—— activity

    activity生命周期 #Activity生命周期###void onCreate()* Activity已经被创建完毕###void onStart()* Activity已经显示在屏幕,但没有得 ...

  4. 原创 C++之常量(一)

    1概述 一个C++程序就是一系列数据与操作的集合.当一个C++程序开始运行的时候,与该程序相关的数据就会被加载到内存中.当数据与内存发生关联的时候,这些数据就会具有如下的特性: 数据在内存中的地址.这 ...

  5. JDWP Agent

    JDWP Agent Implementation Description Revision History Disclaimer 1. About this Document 1.1 Purpose ...

  6. java中 String StringBuffer StringBuilder的区别

    * String类是不可变类,只要对String进行修改,都会导致新的对象生成. * StringBuffer和StringBuilder都是可变类,任何对字符串的改变都不会产生新的对象. 在实际使用 ...

  7. QStatusBar的用法

      QStatusBa,状态栏是位于主窗口的最下方,提供一个显示工具提示等信息的地方.QMainWindow类里面就有一个statusBar()函数,用于实现状态栏的调用.以下例子都在QMainWin ...

  8. 简体中国版文档的Markdown语法

    Markdown文件 注意︰这是简体中国版文档的Markdown语法.如果你正在寻找英语版文档.请参阅Markdown︰ Markdown: Syntax. Markdown: Syntax 概述 哲 ...

  9. Torch7在Ubuntu下的安装与配置

    Torch7的本系列教程的主要目的是介绍Torch的入门使用.今天首先分享一下Torch7的安装.(在Ubuntu14.04安装torch7) 为什么选择Torch Torch的目标是在建立科学算法的 ...

  10. 史上最全QC学习方案,值得收藏!

    Quality Center是一个基于Web的强大的测试管理工具,可以组织和管理应用程序测试流程的所有阶段,**制定测试需求.计划测试.执行测试和跟踪缺陷.此外,通过Quality Center还可以 ...