之前做的一些图上的期望步数的题大多用到高斯消元来求解(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. Hibernate入门详解

    学习Hibernate ,我们首先要知道为什么要学习它?它有什么好处?也就是我们为什么要学习框架技术? 还要知道    什么是Hibernate?    为什么要使用Hibernate?    Hib ...

  2. android 自定义控件——(二)圆形按钮,圆形View

    ----------------------------------↓↓圆形按钮,圆形View(源代码下有属性解释)↓↓---------------------------------------- ...

  3. 解决adobe air sdk打包 apk后自动在包名前面加上air. (有个点)前缀的问题

    早就找到了这个方法,但是一直忙没心思写博客. 默认情况下,所有 AIR Android 应用程序的包名称都带 air 前缀.若不想使用此默认行为,可将计算机环境变量 AIR_NOANDROIDFLAI ...

  4. jquery双向列表选择器select版

    这个是select版的,若想美化某些样式是不支持得,可以用div模拟版的,功能基本实现能用了,需要其他功能自己加上. div模拟版链接:http://www.cnblogs.com/tie123abc ...

  5. Mac下安装Matlab R2015b

    步骤 下载Matlab R2015b链接:http://pan.baidu.com/s/1pLGpUfp 密码:k1xg 下载破解文件r2015b_crackhttp://download.csdn. ...

  6. (二)Spark-Linux环境准备-Java&Python版Spark

    Spark-Linux环境准备 视频教程: 1.优酷 2.YouTube 硬软件环境 1.虚拟机:VMware Workstation 12 2.虚拟机操作系统:RedHat5u4,单核,1G内存,2 ...

  7. Oracle Partition Outer Join 稠化报表

    partition outer join实现将稀疏数据转为稠密数据,举例: with t as (select deptno, job, sum(sal) sum_sal from emp group ...

  8. [MySQL性能优化系列]巧用索引

    1. 普通青年的索引使用方式 假设我们有一个用户表 tb_user,内容如下: name age sex jack 22 男 rose 21 女 tom 20 男 ... ... ... 执行SQL语 ...

  9. 一个典型的MapRuduce实例------webcount(网站统计访客信息)

    统计某一特定网站的某个时辰访客人数 所用版本:hadoop2.6.5 数据样式如下: 111.111.111.111 - - [16/Dec/2012:05:32:50 -0500] "GE ...

  10. My first win32 application program

    #include<afxwin.h>#include<afx.h>#define _AFXDLLclass CHelloApp :public CWinApp{public:  ...