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), ...
随机推荐
- iframe高度自适应(同域)
今天解决了iframe高度自适应的问题,不过这只是同域下的页面嵌入,以下是代码: function SetCwinHeight(){ var iframeid = document.getElemen ...
- 2D动画的制作
通过css3的transform transition可以实现平移,旋转,缩放,拉伸等效果 1.缩放 -webkit-transform: scale(1); -moz-transform: sca ...
- AEAI ESB培训大纲
1. 概述 本文档的目的是为了让使用者能更好的操作.维护.服务于整个ESB系统平台,该信息系统平台不仅需要成熟稳定的产品,更需要技术熟练的运行维护人员,以便能更好地进行科学有效的运行维护工作. AEA ...
- iOS 自定义方法 - 播放GIF
示例代码 ///////////////////////第一种/////////////////////// //// GifView.h// GIFViewer//// Created by ...
- 获取当前应用的系统路径工具类和java的System.getProperty()方法介绍
java的System.getProperty()方法可以获取的值,如下: 对于Java程序,无论是未打包的还是打包的JAR或WAR文件,有时候都需要获取它运行所在目录信息,如何做到这一点呢? /** ...
- jqgrid+bootstrap样式实践
jqgrid+bootstrap样式实践,报错数据加载,选中,删除等功能 需要引入的样式 bootstrap.min.css ui.jqgrid.css 需要引入的JS jquery.min.js b ...
- 00 Cadence学习总目录
这个系列是我学习于博士CADENCE视频教程60讲时,一边学一边记的笔记.使用的CADENCE16.6. 01-03课 了解软件 创建工程 创建元件库 分裂元件的制作方法 04课 正确使用hetero ...
- [LeetCode] Encode and Decode Strings 加码解码字符串
Design an algorithm to encode a list of strings to a string. The encoded string is then sent over th ...
- [LeetCode] First Missing Positive 首个缺失的正数
Given an unsorted integer array, find the first missing positive integer. For example,Given [1,2,0] ...
- JavaScript OOP 之「创建对象」
工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程.工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题. function createPers ...