bzoj1415[NOI2005]聪聪和可可
之前做的一些图上的期望步数的题大多用到高斯消元来求解(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]聪聪和可可的更多相关文章
- 【BZOJ1415】【NOI2005】聪聪和可可(动态规划,数学期望)
[BZOJ1415][NOI2005]聪聪和可可(动态规划,数学期望) 题面 BZOJ 题解 先预处理出当可可在某个点,聪聪在某个点时 聪聪会往哪里走 然后记忆化搜索一下就好了 #include< ...
- 【bzoj1415】 Noi2005—聪聪和可可
http://www.lydsy.com/JudgeOnline/problem.php?id=1415 (题目链接) 题意 一张图,聪聪想吃可可.每单位时间聪聪可以先移动两次:可可后移动一次或停在原 ...
- 【BZOJ1415】 [Noi2005]聪聪和可可 概率与期望
其实题不难,不知提交了几次...不能代码MD...注意一些基本问题...SB概率题 #include <iostream> #include <cstdio> #include ...
- BZOJ1415[Noi2005]聪聪和可可——记忆化搜索+期望dp
题目描述 输入 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下来E行 ...
- 【bzoj1415】[Noi2005]聪聪和可可 期望记忆化搜索
题目描述 输入 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下来E行 ...
- BZOJ1415 [Noi2005]聪聪和可可 【SPFA + 期望dp记忆化搜索】
题目 输入格式 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下来E行 ...
- bzoj1415 [Noi2005]聪聪和可可【概率dp 数学期望】
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1415 noip2016 D1T3,多么痛的领悟...看来要恶补一下与期望相关的东西了. 这是 ...
- [BZOJ1415][NOI2005]聪聪与可可
Description Input 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点 ...
- 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), ...
随机推荐
- Css3新特性应用之形状
一.自适应椭圆 * border-radius特性: * 可以单独指定水平和垂直半径,并且值可以是百分比,用/(斜杠)分隔这两个值即可(可以实现自适应宽度椭圆). * 还可以单独指定四个角 ...
- JS正则表达式(JavaScript regular expression)
RegExp直接量和对象的创建 就像字符串和数字一样,程序中每个取值相同的原始类型直接量均表示相同的值,这是显而易见的.程序运行时每次遇到对象直接量(初始化表达式)诸如{}和[]的时候都会创建新对象. ...
- Atitit.http httpclient实践java c# .net php attilax总结
Atitit.http httpclient实践java c# .net php attilax总结 1. Navtree>> net .http1 2. Httpclient理论1 2. ...
- Android中的自定义控件(一)
自定义控件是根据自己的需要自己来编写控件.安卓自带的控件有时候无法满足你的需求,这种时候,我们只能去自己去实现适合项目的控件.同时,安卓也允许你去继承已经存在的控件或者实现你自己的控件以便优化界面和创 ...
- Android LocalBroadcastManager 的使用总结
转载请标明出处:http://www.cnblogs.com/zhaoyanjun/p/6048369.html 本文出自[赵彦军的博客] 前言 在Android中,Broadcast是一种广泛运用的 ...
- Node.js学习——HTTP
HTTP Node.js开发的目的就是为了用JavaScript编写Web服务器程序.因为JavaScript实际上已经统治了浏览器端的脚本,其优势就是有世界上数量最多的前端开发人员.如果已经掌握了J ...
- C#语言基础——函数
函数一个较大的程序一般应分为若干个程序块,每一个模块用来实现一个特定的功能.所有的高级语言中都有子程序这个概念,用子程序实现模块的功能.在C#语言中,子程序的作用是由一个主函数和若干个函数构成.由主函 ...
- hadoop常用的操作命令
1.显示hdfs上test目录下的所有文件列表 hadoop fs -ls /test/ 2.查看hdfs中的文件内容 hadoop fs -cat /daas/bstl/term/rawdt/201 ...
- mysql主备(centos6.4)
服务器基本环境: 两台centos6.4.iptables diabled .selinux disabled 两台的hosts解析 #yum install mysql -y //这个一定要装 ...
- WPF 自定义搜索框
控件中的搜索图标下载地址:http://www.easyicon.net/1183666-Search_icon.html 搜索框设计过程比较简单: 1.先定义一个Rectangle作为背景 2. ...