【HNOI2013】游走

Description

给出一张n个点,m条边的无向连通图。有一个人从点1开始随机游走,到点n结束。他每走过一条边就会得到其编号的分数。(可以重复走而重复得分)。现在让你安排每条边的编号,让他的得分期望值最小。求这个最小值。
n<=500

Solution

我们发现只要求出每条边的期望经过次数,然后从大到小排序,依次编号,就一定是最小值了。(即期望大的编号要小)
但是,每条边的期望经过次数要怎么求呢?
这个东西非常麻烦,于是我们可以另辟巧径。
如果我们求出了每个点的期望经过次数?
那么每条边的期望经过次数 ei 就等于 pxdx+pydy
pi 为每个点的期望经过次数, di 为每个点的出度。
这个很显然…
那么怎么求呢?
高斯消元!!
对于每个非1非n的点, pi=i>jpjdj
1点因为一开始就在,所以要加1。
n点应该只有1,但是应为要避免它对其它点的影响,就变成0了。(因为走到n就走不出来了)
然后这道题就完美解决了!

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 505
#define db double
using namespace std;
struct note{
    db a[N],b;
}matrix[N];
int l,n,m,t[N*N],next[N*N],last[N],c[N],d[N],bz[N],x[N*N],y[N*N];
db an[N],e[N*N],ans;
void add(int x,int y) {
    t[++l]=y;c[x]++;next[l]=last[x];last[x]=l;
}
void gauss() {
    fo(i,1,n-1) {
        fo(j,i+1,n) if (abs(matrix[j].a[i])>abs(matrix[i].a[i])) 
        swap(matrix[i],matrix[j]);
        fo(j,i+1,n) {
            db f=matrix[j].a[i]/matrix[i].a[i];
            fo(k,i,n) matrix[j].a[k]=matrix[i].a[k]*f-matrix[j].a[k];
            matrix[j].b=matrix[i].b*f-matrix[j].b;
        }
    }
    fd(i,n,2) {
        fo(j,1,i-1) {
            db f=matrix[j].a[i]/matrix[i].a[i];
            fo(k,j,n) matrix[j].a[k]=matrix[i].a[k]*f-matrix[j].a[k];
            matrix[j].b=matrix[i].b*f-matrix[j].b;
        }
    }
    fo(i,1,n) an[i]=matrix[i].b/matrix[i].a[i];
}
int main() {
    scanf("%d%d",&n,&m);
    fo(i,1,m) scanf("%d%d",&x[i],&y[i]),add(x[i],y[i]),add(y[i],x[i]);
    int i=0,j=1;d[1]=1;bz[1]=1;
    matrix[n].a[n]=matrix[1].a[1]=matrix[1].b=1;
    fo(i,1,n-1) {
        matrix[i].a[i]=1;
        rep(j,i) matrix[i].a[t[j]]=-1.0/c[t[j]];
    }
    gauss();
    fo(i,1,m) e[i]=an[x[i]]/c[x[i]]+an[y[i]]/c[y[i]];
    sort(e+1,e+m+1);
    fo(i,1,m) ans=(ans+e[i]*(m-i+1));
    printf("%.3lf",ans);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值