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

3143: [Hnoi2013]游走

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

题解 :dp[ u ] 表示以 u 为出发点的期望次数,s[ v ] 表示 v 点的度, e[ i ] 表示经过第 i 条边的期望次数,edge[ i ] 表示边集。

对于dp[ u ] 我们对每个u 点列一个方程(根据下图的的等式),然后高斯消元得到答案,再计算出对每一条边计算出它的 e[ i ] ,最后贪心一下就得到答案了。

注意精度!!!

#include<iostream>
#include<sstream>
#include<iterator>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<bitset>
#include<climits>
#include<queue>
#include<iomanip>
#include<cmath>
#include<stack>
#include<map>
#include<ctime>
#include<new>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define MT(a,b) memset(a,b,sizeof(a))
const int INF  =  0x3f3f3f3f;
const int O    =  1e6;
const int mod  =  1e6 + 3;
const int maxn =  5e2 + 5;
const double PI  =  acos(-1.0);
//const double E   =  2.718281828459;
const long double eps = 1e-10;

struct Matrix {
    void clear() { for(int i=0; i<maxn; i++) for(int j=0; j<maxn; j++) a[i][j] = 0; }
    int equ, var; // equ 表示等式个数,var 表示未知变量个数
    long double a[maxn][maxn], x[maxn]; // a表示增广矩阵, x表示解

    int GetTriangle() {
        int i = 0;
        for(int col=0; i<equ && col<var; i++, col++) { // 对第i行进行消元

            // 获得最大行,并与第i行交换
            long double max_row = fabs(a[i][col]);
            int row = i;
            for(int k=i+1; k<equ; k++)
                if(max_row < fabs(a[k][col])) max_row = fabs(a[row=k][col]);
            for(int j=col; j<=var; j++) swap(a[row][j], a[i][j]);
            if(fabs(a[i][col]) < eps) { i --; continue; }

            // 消元
            for(int k=i+1; k<equ; k++) {
                if(fabs(a[k][col]) < eps) continue;
                long double p = a[k][col] / a[i][col];
                for(int j=col; j<=var; j++) a[k][j] -= a[i][j] * p;
            }
        }
        return i;
    }

    int Gauss(){
        int k = GetTriangle();
        for(int i=var-1; i>=0; i--) {
            long double y = a[i][var];
            for(int j=var-1; j>i; j--) y -= x[j] * a[i][j];
            x[i] = y / a[i][i];
        }
        return 0; // 唯一解
    }

} M;

struct dd { int to, next; }E[maxn * maxn];
int head[maxn], cnt = 0;
void add(int u, int v) { E[cnt] = {v, head[u]}; head[u] = cnt ++; }

int s[maxn];
pair<int, int>edge[maxn * maxn];
long double e[maxn * maxn];

int main(){
    int n, m; scanf("%d%d", &n, &m);
    cnt = 0;
    for (int i = 0; i < maxn; i++) s[i] = 0, head[i] = -1;
    for (int i = 0; i < m; i++) {
        int u, v; scanf("%d%d", &u, &v);
        s[--u]++; s[--v]++;
        add(u, v);
        edge[i] = {u, v};
    }

    M.clear();
    M.var = M.equ = n;
    for (int u = 0; u < n; u++) {
        M.a[u][u] = 1.0;
        for (int i = head[u]; ~i; i = E[i].next) {
            int v = E[i].to;
            M.a[u][v] -= 1.0 / s[v];
            M.a[v][u] -= 1.0 / s[u];
        }
    }
    M.a[0][n] = 1.0;
    for (int i = 1; i < n; i++) M.a[i][n] = 0.0;
    for (int j = 0; j < n; j++) M.a[n - 1][j] = 0.0;
    M.a[n - 1][n - 1] = 1.0;
    M.Gauss();

    for (int i = 0; i < m; i++) {
        int u = edge[i].first;
        int v = edge[i].second;
        e[i] = M.x[u] / s[u] + M.x[v] / s[v];
    }

    sort(e, e + m);
    long double ans = 0;
    for (int i = 0; i < m; i++) ans += e[i] * (m - i);
    printf("%.3Lf\n", ans);
    return 0;
}

最后贪心一下就得到答案了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值