BZOJ 3143 HNOI2013 游走 高斯消元 期望

这道题是我第一次使用高斯消元解决期望类的问题,首发A了,感觉爽爽的....

中文题目,就不翻大意了,直接给原题:

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

  小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。

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

  输出最小的总分期望值。

Solution:

  这题贪心很明显,哪条边走过次数的期望最大,它就应该获得最小的编号。

  所以假设我们已经求出了每条边走过的期望,我们就可以给它们并编上号了。

  怎么算出每条边走过的期望呢?

  每条边连接着两个点u,v,很明显的,当我们经过这条边,一定是从两个点中的某一个进入。

  所以走过边l的期望=走过u点的期望次数*从u点走到l上的概率+走过v点的期望次数*从v点走到l上的概率 (其中从i点走到它连接边的概率为1/d[i],d[i]为i的度数)

  即:E[l]=e[u]/d[u]+e[v]/d[v]

  可是我们只知道e[n]=0。但我们还知道这些点之间哪些是连通的,从而可以得出它们之间的关系:

  

  我们就可以利用这些点之间的关系建立起方程组,从而使用高斯消元求解。

  别忘了,点求解完还要带回到每条边上去哦....

  

  附Bzoj上的AC代码(codevs上过不了...我也不知道为什么...)

  

/*
  Problem : Bzoj 3143 概率 & 高斯消元
  Author : Robert Yuan
  Memory : 15604 kb
  Time : 628 MS
  Result : Accept
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;

#define maxn 520

struct Node{
    int data,next;
}node[maxn*maxn<<1];

struct Edge{
    int u,v;
    double w;
}edge[maxn*maxn<<1];

#define now node[point].data
#define then node[point].next

int n,m,cnt;
int head[maxn],deg[maxn];
const double eps=1e-6;
double w[maxn][maxn],rec_x[maxn],ans;

bool cmp(const Edge A,const Edge B){
    return A.w>B.w;
}

inline int in(){
    int x=0;char ch=getchar();
    while(ch>‘9‘ || ch<‘0‘) ch=getchar();
    while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
    return x;
}

void add(int u,int v){
    node[++cnt].data=v;node[cnt].next=head[u];deg[u]++;head[u]=cnt;
    node[++cnt].data=u;node[cnt].next=head[v];deg[v]++;head[v]=cnt;
}

void prework(){
    n=in();m=in();
    int u,v;
    for(int i=1;i<=n;i++) head[i]=-1;
    for(int i=1;i<=m;i++)
        u=in(),v=in(),edge[i].u=u,edge[i].v=v,add(u,v);
    int point;
    for(int i=1;i<=n;i++){
        w[i][i]=1;
        point=head[i];
        while(point!=-1){
            w[i][now]=-(double)1/deg[now];
            point=then;
        }
    }
    w[1][n+1]=1;
}

void Swap(int i,int j,int x){
    double t;
    for(int k=x+1;k<=n+1;k++)
        t=w[i][k],w[i][k]=w[j][k],w[j][k]=t;
}

void gauss(){
    int i,j;
    for(i=1,j=1;i<=n && j<=n;i++,j++){
        int max_r=i;
        for(int k=i+1;k<=n;k++)
            if(fabs(w[max_r][j])+eps<fabs(w[k][j]))
                max_r=k;
        if(fabs(w[max_r][j])<eps){i--;continue;}
        if(max_r!=i) Swap(i,max_r,j);
        for(int k=i+1;k<=n;k++){
            double rate=w[k][j]/w[i][j];
            w[k][j]=0;
            for(int l=j+1;l<=n+1;l++)
                w[k][l]-=w[i][l]*rate;
        }
    }

    for(int i=n;i>=1;i--)
        if(fabs(w[i][i])>eps){
            double ans_c=w[i][n+1];
            for(int k=i+1;k<=n;k++)
                ans_c-=w[i][k]*rec_x[k];
            rec_x[i]=ans_c/w[i][i];
        }
}

void mainwork(){
    gauss();
    for(int i=1;i<=m;i++){
        edge[i].w=rec_x[edge[i].u]/deg[edge[i].u]+rec_x[edge[i].v]/deg[edge[i].v];
    }
    sort(edge+1,edge+m+1,cmp);
    for(int i=1;i<=m;i++)
        ans+=edge[i].w*i;
    printf("%.3lf",ans);
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("x.in","r",stdin);
#endif
    prework();
    mainwork();
    return 0;
}

时间: 2024-10-26 17:05:04

BZOJ 3143 HNOI2013 游走 高斯消元 期望的相关文章

BZOJ 3143: [Hnoi2013]游走( 高斯消元 )

我一开始的想法是设f(x)表示点x到N路径的期望长度, 那么f(u) = (∑f(v)+w(u,v)) / degreeu, f(N)=0, 我们代入入消元应该可以得到f(1)关于各条边长的关系式f(1)=∑we..然后贪心, 按照他们的系数来给边权...但是不会实现..但是我感觉是可行的..PoPoQQQ题解:http://blog.csdn.net/PoPoQQQ/article/details/42234607 ---------------------------------------

【BZOJ-3143】游走 高斯消元 + 概率期望

3143: [Hnoi2013]游走 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2264  Solved: 987[Submit][Status][Discuss] Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数.当小Z 到达N号顶点时游走结束,总分为所有获得的分数

[BZOJ 3143][Hnoi2013]游走(高斯消元+期望)

Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数.当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和. 现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小. Solution 对于点u(u≠1):到达u的概率 f[u]=∑f[v]/d[v] (Edges(u,v)) 而f[1]=∑f[v]/d[v]+1

BZOJ 3143 HNOI2013 游走 期望DP+高斯消元

题目大意:给定一个无向连通图,我们需要给每条边附一个1~m的不重复的权值,使1到n的期望权值和最小 首先贪心思想是求出每条边的期望经过次数 然后对期望值最小的边附加m的权值,第二小的边附加m-1的权值,以此类推. 令f[i]为第i个点的期望经过次数 那么每条边的期望经过次数就是f[x]/d[x]+f[y]/d[y] 其中d[x]表示x的度数 那么显然有: f[1]=1+Σ[1->j]f[j]/d[j] f[i]=Σ[i->j]f[j]/d[j] (2<=i<=n-1) 其中f[n]

bzoj 3143 [Hnoi2013]游走【高斯消元+dp】

参考:http://blog.csdn.net/vmurder/article/details/44542575 和2337有点像 设点u的经过期望(还是概率啊我也分不清,以下都分不清)为\( x[u] \) ,度为 \( in[u] \),边\( (u,v) \) 的经过期望为 \( \frac{x[u]}{in[u]}+\frac{x[v]}{in[v]} \) 那么转换为求每个点的经过期望,\( x[u]=\sum_{v}^{v\subset son(u)}\frac{x[v]}{in[v

[BZOJ 3143][HNOI2013]游走(数学期望)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3143 分析: 易得如果知道了每条边经过的数学期望,那就可以贪心着按每条边的期望的大小赋值,所以问题就是如何求每条边的期望. 直接求没办法求的,可以先求出每个点经过的期望. 易得f[i]=∑f[j]/d[j] j->i有边 特殊的,对于起点,因为刚开始就在,所以应该是f[1]=1+∑f[j]/d[j]:对于终点,到了终点后不能再到其他节点,所以对其他边并没有贡献,所以f[n]=0 然后

●BZOJ 3143 [Hnoi2013]游走

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3143题解: 期望dp,高斯消元 首先有这样一种贪心分配边的编号的方案:(然后我没想到,233) 我们按每一条边的期望经过次数去分配编号, 具体来说,就是期望经过次数越多的边,分配的编号越小,反之则编号越大. 然后问题转化为如何求一条边的期望经过次数.(把求边的期望转化为求点的期望) 我们定义cnt[i]表示i点的出度,dp[i]表示期望经过i点的次数. 然后对于一个边(u,v),期望经过该

bzoj 3143: [Hnoi2013]游走

高斯消元 对于边可能很多,那我们计录点的期望次数就行了. 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<queue> 7 #include<algorithm> 8 #include<vector> 9 #define M 1009 10 #

【BZOJ】3143: [Hnoi2013]游走 期望+高斯消元

[题意]给定n个点m条边的无向连通图,每条路径的代价是其编号大小,每个点等概率往周围走,要求给所有边编号,使得从1到n的期望总分最小(求该总分).n<=500. [算法]期望+高斯消元 [题解]显然,应使经过次数越多的边编号越小,问题转化为求每条边的期望经过次数. 边数太多,容易知道f(u,v)=f(u)/out(u)+f(v)/out(v),所以转化为求每个点的期望经过次数,这就是驱逐猪猡了. 设f[x]表示点x的期望经过次数,根据全期望公式(讨论"经过"的问题不能依赖于下一步