您的位置:首页 > 其它

[期望DP+高斯消元] BZOJ3143: [Hnoi2013]游走

2017-05-20 12:11 417 查看

题意

一个无向连通图,顶点从1编号到N,边从1编号到M。

小Z在该图上进行随机游走,初始时小Z在1号顶点。

每一步小Z以相等的概率随机选择从当前顶点出去的某条边走,并获得等于这条边的编号的分数。

当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。

现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

n<=500

题解

直接算题目要求的东西有点难办。

首先边权的分配与小Z随机游走没有任何关系,

如果我们能知道每条边经过的期望次数,按期望从大到小给边编号1~m即可。

问题来了,如何才能知道每条边期望走的次数呢?

这里需要一步很妙的转化,设E[i]为点i的期望访问次数,d[i]为点i的出度,E(x,y)表示边x–y的期望经过次数,则有:E(x,y)=E[x]/d[x]+E[y]/d[y]

这样问题就转化为求E[i],建立期望之间的关系,高斯消元即可。

复杂度O(n3)

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=505,maxe=maxn*maxn;
struct Edge{
int x,y; double w;
bool operator < (const Edge &b)const{
return w<b.w;
}
} Es[maxe];
int n,m,d[maxn];
double ans,E[maxn],K[maxn][maxn];
void Gauss(){
n--;
for(int i=1;i<=n;i++){
int where=0;
for(int j=i;j<=n;j++) if(fabs(K[j][i])>fabs(K[where][i])) where=j;
for(int j=0;j<=n;j++) swap(K[where][j],K[i][j]);
for(int j=i+1;j<=n;j++){
double t=K[j][i]/K[i][i];
for(int k=0;k<=n;k++) K[j][k]-=t*K[i][k];
}
}
for(int i=n;i>=1;i--){
E[i]=-K[i][0];
for(int j=i+1;j<=n;j++) E[i]-=K[i][j]*E[j];
E[i]/=K[i][i];
}
n++;
}
int main(){
freopen("bzoj3143.in","r",stdin);
freopen("bzoj3143.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d",&Es[i].x,&Es[i].y), d[Es[i].x]++, d[Es[i].y]++;
for(int i=1;i<=m;i++) if(Es[i].x!=n&&Es[i].y!=n){
K[Es[i].x][Es[i].y]+=1.0/d[Es[i].y];
K[Es[i].y][Es[i].x]+=1.0/d[Es[i].x];
}
K[1][0]=1;
for(int i=1;i<=n-1;i++) K[i][i]=-1;
Gauss();
for(int i=1;i<=m;i++)
Es[i].w=E[Es[i].x]/d[Es[i].x]+E[Es[i].y]/d[Es[i].y];
sort(Es+1,Es+1+m);
for(int i=1;i<=m;i++) ans+=Es[i].w*(m-i+1);
printf("%.3lf",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: