bzoj1415[NOI2005]聪聪和可可

时间:2022-09-20 16:29:57

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