这道题是我第一次使用高斯消元解决期望类的问题,首发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<<]; struct Edge{
int u,v;
double w;
}edge[maxn*maxn<<]; #define now node[point].data
#define then node[point].next int n,m,cnt;
int head[maxn],deg[maxn];
const double eps=1e-;
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=;char ch=getchar();
while(ch>'' || ch<'') ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',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=;i<=n;i++) head[i]=-;
for(int i=;i<=m;i++)
u=in(),v=in(),edge[i].u=u,edge[i].v=v,add(u,v);
int point;
for(int i=;i<=n;i++){
w[i][i]=;
point=head[i];
while(point!=-){
w[i][now]=-(double)/deg[now];
point=then;
}
}
w[][n+]=;
} void Swap(int i,int j,int x){
double t;
for(int k=x+;k<=n+;k++)
t=w[i][k],w[i][k]=w[j][k],w[j][k]=t;
} void gauss(){
int i,j;
for(i=,j=;i<=n && j<=n;i++,j++){
int max_r=i;
for(int k=i+;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+;k<=n;k++){
double rate=w[k][j]/w[i][j];
w[k][j]=;
for(int l=j+;l<=n+;l++)
w[k][l]-=w[i][l]*rate;
}
} for(int i=n;i>=;i--)
if(fabs(w[i][i])>eps){
double ans_c=w[i][n+];
for(int k=i+;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=;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+,edge+m+,cmp);
for(int i=;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 ;
}

[以下是笔者后来发现的问题]

  首先感谢某位不愿意透露姓名的人堆同学复制了我的代码,然后代入了样例,结果:

  

  然后第三行是无解?可是答案却能跑出来...于是我傻了...开始胡乱吹逼[毕竟这么久没打了...]

  

  好吧,然后各种逼都被打败了...(●—●)

  只能认真看看到底出了什么问题,于是发现这个式子有奥妙:

  

  左边的e[i]表示走到i点的期望,右边的e[j]表示走出j点的期望。

  “走到”和“走出”却并不是一样的!

  我们设e[i]表示走到i点的概率,e'[i]表示走出i点的概率。

  如果说i!=n那么走到就能走出,e[i]=e'[i];

  如果i==n那么就有e'[n]=0,e[n]=1他们俩不同...尽管都已知,而我们列式子的时候,将e'[n]作为未知数带进别的点的式子里,但在n自己的式子中却用的是e[n],导致两个变量混淆。

  所以鉴于e'[n]=0,就将建立方程部分修改了一下:

  

  于是现在的式子就发生了变化,最后化出来的矩阵也变成了正常的样子:

  

  这样解出来的就是e[n]是到达n点的期望=1,当然在给边设定权值的时候,我们用的都是e'[i],所以我们手动修改一下e'[n]=0就好了...

  下面是修改过后的代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm> using namespace std; #define maxn 520 struct Node{
int data,next;
}node[maxn*maxn<<]; struct Edge{
int u,v;
double w;
}edge[maxn*maxn<<]; #define now node[point].data
#define then node[point].next int n,m,cnt;
int head[maxn],deg[maxn];
const double eps=1e-;
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=;char ch=getchar();
while(ch>'' || ch<'') ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',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=;i<=n;i++) head[i]=-;
for(int i=;i<=m;i++)
u=in(),v=in(),edge[i].u=u,edge[i].v=v,add(u,v);
int point;
for(int i=;i<=n;i++){
w[i][i]=;
point=head[i];
while(point!=-){
if(now!=n) w[i][now]=-(double)/deg[now];
else w[i][now]=;
point=then;
}
}
w[][n+]=;
} void Swap(int i,int j,int x){
double t;
for(int k=x+;k<=n+;k++)
t=w[i][k],w[i][k]=w[j][k],w[j][k]=t;
} void gauss(){
int i,j;
for(i=,j=;i<=n && j<=n;i++,j++){
int max_r=i;
for(int k=i+;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+;k<=n;k++){
double rate=w[k][j]/w[i][j];
w[k][j]=;
for(int l=j+;l<=n+;l++)
w[k][l]-=w[i][l]*rate;
}
} for(int i=n;i>=;i--)
if(fabs(w[i][i])>eps){
double ans_c=w[i][n+];
for(int k=i+;k<=n;k++)
ans_c-=w[i][k]*rec_x[k];
rec_x[i]=ans_c/w[i][i];
}
rec_x[n]=;
} void mainwork(){
gauss();
for(int i=;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+,edge+m+,cmp);
for(int i=;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 ;
}

最后再次鸣谢人堆同学提出的问题,有问题才有进步嘛,欢迎大家提问哦...

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

  1. bzoj 3143: [Hnoi2013]游走 高斯消元

    3143: [Hnoi2013]游走 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1026  Solved: 448[Submit][Status] ...

  2. Luogu3232 HNOI2013 游走 高斯消元、期望、贪心

    传送门 这种无向图上从一个点乱走到另一个点的期望题目好几道与高斯消元有关 首先一个显然的贪心:期望经过次数越多,分配到的权值就要越小. 设$du_i$表示$i$的度,$f_i$表示点$i$的期望经过次 ...

  3. BZOJ3143:[HNOI2013]游走(高斯消元)

    Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点, ...

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

    3143: [Hnoi2013]游走 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2264  Solved: 987[Submit][Status] ...

  5. BZOJ 3143: [Hnoi2013]游走 概率与期望+高斯消元

    Description 一个无向连通图,顶点从1编号到N,边从1编号到M.小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获 ...

  6. BZOJ.3143.[HNOI2013]游走(概率 期望 高斯消元)

    题目链接 参考 远航之曲 把走每条边的概率乘上分配的标号就是它的期望,所以我们肯定是把大的编号分配给走的概率最低的边. 我们只要计算出经过所有点的概率,就可以得出经过一条边(\(u->v\))的 ...

  7. bzoj 3143 [Hnoi2013]游走 期望dp+高斯消元

    [Hnoi2013]游走 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3394  Solved: 1493[Submit][Status][Disc ...

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

    参考:http://blog.csdn.net/vmurder/article/details/44542575 和2337有点像 设点u的经过期望(还是概率啊我也分不清,以下都分不清)为\( x[u ...

  9. [HNOI2013][BZOJ3143] 游走 - 高斯消元

    题目描述 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边 ...

随机推荐

  1. 【转】详解JavaScript中的this

    ref:http://blog.jobbole.com/39305/ 来源:foocoder 详解JavaScript中的this JavaScript中的this总是让人迷惑,应该是js众所周知的坑 ...

  2. 启动hadoop报ERROR org.apache.hadoop.hdfs.server.namenode.FSImage: Failed to load image from FSImageFile

    不知道怎么回事,今天在启动集群时通过jps查看进程时始终有一个standby namenode进程无法启动.查看日志时报的是不能加载fsimage文件.日志截图如下: 日志报的很明显了是不能加载元数据 ...

  3. 深入浅出MongoDB(一)NoSQL

    从本文开始,我们一起学习一下MongoDB相关内容,在学习MongoDB之前,首先要做的就是学习NoSQL. 为什么要学习NoSQL,原因很简单,因为MongoDB是NoSQL数据库的一种,换言之,如 ...

  4. Sublime Text 2 安装与使用SFTP插件

    http://wbond.net/sublime_packages/sftp/usage http://www.360doc.com/content/13/0603/15/9437165_290170 ...

  5. 【深入比较ThreadLocal模式与synchronized关键字】

    [深入比较ThreadLocal模式与synchronized关键字]ThreadLocal模式与synchronized关键字都是用于处理多线程并发访问变量的问题.只是两者处理问题的角度和思路不同. ...

  6. Google账户无法登陆-Solved

    Author:KillerLegend Date:2014.5.19 From:http://www.cnblogs.com/killerlegend/p/3737888.html 这几天不知道怎么回 ...

  7. Python学习教程(learning Python)--3 Python分支结构和布尔逻辑

    本章节主要探讨研究Python下的分支选择结构程序设计问题.   if语句用来检验一个条件, 如果 条件为真,我们运行一块语句(称为 if-块 ), 否则 我们处理另外一块语句(称为 else-块 ) ...

  8. Python学习教程(learning Python)--1.3 Python数据输入

    多数应用程序都有数据输入语句,用于读入数据,和用户进行交互,在Python语言里,可以通过raw_input函数实现数据的从键盘读入数据操作. 基本语法结构:raw_input(prompt) 通常p ...

  9. <! [if IE 神奇的条件注释 ]>

    早上起来无聊,看到某学长发的一张代码截图有条件注释,正好,研究一下. 条件注释: 在IE中用来区分IE版本.是否为IE的代码神器! 在其他的浏览器里是不好使的. 不过也值得了,IE都区分出来了,其他的 ...

  10. Objective-C关于数据处理

    本文介绍如何在Objective-C中操作数据.我们将使用数组.指针.字符串等. 数组是数据项的一个集合,这些数据项叫做元素,我们可以用一个数组索引来引用元素.例如,如果把数字存储在一个名为array ...