您的位置:首页 > 其它

bzoj3143 游走 [高斯消元+概率]

2017-03-12 09:45 162 查看

Description

一个无向连通图,顶点从1编号到N,边从1编号到M。 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z到达N号顶点时游走结束,总分为所有获得的分数之和。 现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

Input

第一行是正整数N和M,分别表示该图的顶点数和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。

Output

仅包含一个实数,表示最小的期望值,保留3位小数。

Sample Input

3 3

2 3

1 2

1 3

Sample Output

3.333

HINT

边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。

Solution

这道题要求编号后期望的最小得分,为了使得分最小,那么就应该让出现期望大的边编号尽量小.这样我们就把问题转换成了求边的期望出现概率,然后贪心地编号即可.我们接着考虑,如何求边的期望出现概率呢?我们可以通过求出这两条边端点的出现概率除以各自度数然后加和,于是我们顺利把这道题转换成了求点出现概率的题目.对每个点列方程,高斯消元求解既可,需要特别注意的是起始点求概率时要+1,因为它是一定会出现的!

Code

#include<bits/stdc++.h>
#define maxn 3010
#define eps 1e-6
// #define COGS
// #define DEBUG
using namespace std;
int n,m,num_edge=0;
int edge[maxn][maxn];
double answer=0;
double a[maxn][maxn],ans[maxn],deg[maxn];

inline int dcmp(double x)
{
if(abs(x)<=eps) return 0;
else return x>0?1:-1;
}

inline int read()
{
char ch;
int read=0,sign=1;
do
ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-');
if(ch=='-') sign=-1,ch=getchar();
while(ch>='0'&&ch<='9')
{
read=read*10+ch-'0';
ch=getchar();
}
return sign*read;
}

void pre_work()
{
n=read(),m=read();n--;
for(int i=1,u,v;i<=m;++i) u=read(),v=read(),edge[u][v]=edge[v][u]=1,deg[u]++,deg[v]++;
for(int i=1;i<=n;++i)
{
a[i][i]=1,deg[i]=1.0/deg[i];
}
for(int i=1;i<=n;++i)
for(int j=1;j<=n+1;++j)
if(edge[i][j])
a[j][i]-=deg[i];
a[1][n+1]=1;
#ifdef DEBUG
cerr<<"begin"<<endl;
for(int i=1;i<=n;++i){
for(int j=1;j<=n+1;++j)
cerr<<a[i][j]<<" ";
cerr<<endl;}
#endif
}

void print()
{
int cnt=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n+1;++j)
if(edge[i][j])
{
ans[++cnt]=a[i][n+1]*deg[i];
ans[cnt]+=a[j][n+1]*deg[j];
edge[j][i]=0;
}
sort(ans+1,ans+cnt+1);
#ifdef DEBUG
cerr<<"ans[]:"<<endl;
for(int i=1;i<=cnt;++i) cerr<<ans[i]<<endl;
#endif
for(int i=1;i<=cnt;++i) answer+=ans[i]*(cnt-i+1);
printf("%.3lf\n",answer);
}

void gauss()
{
for(int i=1;i<=n;++i)
{
if(!dcmp(a[i][i]))
{
int k=i;
while(!dcmp(a[k][k])) k++;
swap(a[k],a[i]);
}
for(int j=i+1;j<=n;++j)
{
for(int k=i+1;k<=n+1;++k)
a[j][k]-=a[i][k]*(a[j][i]/a[i][i]);
a[j][i]=0;
}
}
for(int i=n;i>1;--i)
{
for(int j=1;j<i;++j)
{
a[j][n+1]-=a[i][n+1]*(a[j][i]/a[i][i]);
a[j][i]=0;
}
a[i][n+1]/=a[i][i];
}
#ifdef DEBUG
cerr<<"after"<<endl;
for(int i=1;i<=n;++i){
for(int j=1;j<=n+1;++j)
cerr<<a[i][j]<<" ";
cerr<<endl;}
#endif
print();
}

int main()
{
#ifdef COGS
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
#endif
pre_work();
gauss();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  noi 高斯消元