BZOJ-3143/洛谷3232 游走(HNOI2013)概率DP

题意:给定n个点m条边。每条边的权值还没决定,权值大小为从1到m。从1出发每次等概率选一条出边向下走,直到走到n点停止,路径代价就是边权总和。由你来决定边权来使得上诉路径代价期望值最小。

解法:点这么少边这么多,比较容易想到我们先求出点的期望经过次数dp[i],那么对于一条边(u,v)它的期望经过次数就等于 (dp[u]/du[u]+dp[v]/du[v])。对于这个值排序之后贪心赋予边权大小即可。

问题在于怎么得到每个点的期望经过次数呢?

这里直接写状态转移方程  dp[u]=sigma(dp[v]/du[v]) + [u==1];(v表示出点,du表示度数)

特别要注意几个点:

①为什么是du[v]而不是du[u]?因为此时算的是从1出发的  。②为什么u==1时候才加一,其实这个问题结果和上一个一样。 ③注意终点n不参与上诉的转移,为什么? 因为一旦到达终点n之后就不再游走了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=500+10;
const double eps=1e-6;
int n,m,k;
vector<int> G[N];
struct edge{
    int x,y; double gx=0;
    bool operator < (const edge &rhs) const {
        return gx>rhs.gx;
    }
}e[N*N];

double c[N][N],b[N];
void Gauss() {
    int r=0;
    for (int i=1;i<=n;i++) {
        int j=r+1;
        while (j<=m && fabs(c[j][i])<eps) j++;  //从下面方程找一个第i位不为0的 
        if (j==m+1) continue;  //不存在第i位不为0的方程 
        r++;  //矩阵的秩
        for (int k=1;k<=n;k++) swap(c[r][k],c[j][k]);  //存在第i位不为0的方程,交换上去 
        swap(b[r],b[j]);
        
        for (int j=1;j<=m;j++) {  //以r方程回代m个方程 
            if (r==j) continue;
            double rate=c[j][i]/c[r][i];
            for (int k=i;k<=n;k++) c[j][k]-=c[r][k]*rate;
            b[j]-=b[r]*rate;
        }  
    }
    
//    for (int i=1;i<=m;i++) if (b[i]) {  //判断无解情况 
//        bool ok=0;
//        for (int j=1;j<=n;j++) if (c[i][j]) { ok=1; break; }
//        if (!ok) { puts("Inconsistent data."); return; }
//    }
    
    //if (r<n) { puts("Multiple solutions."); return; }  //有解但多解
    
    for (int i=1;i<=n;i++) b[i]=b[i]/c[i][i];  //唯一解求解 
}

int main()
{
    cin>>n>>k; m=n;
    for (int i=1;i<=k;i++) {
        int x,y; scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
        e[i].x=x; e[i].y=y; e[i].gx=0;
    }
    memset(c,0,sizeof(c));
    memset(b,0,sizeof(b));
    for (int i=1;i<=n;i++) {
        c[i][i]+=1.0;
        if (i==n) break;
        for (int j=0;j<G[i].size();j++) {
            int v=G[i][j];
            if (v==n) continue;
            c[i][v]-=1.0/G[v].size();
        }
        if (i==1) b[i]+=1.0;
    }
    Gauss();
    for (int i=1;i<=k;i++) {
        e[i].gx+=b[e[i].x]/G[e[i].x].size();
        e[i].gx+=b[e[i].y]/G[e[i].y].size();
    }
    sort(e+1,e+k+1);
    double ans=0;
    for (int i=1;i<=k;i++) ans+=e[i].gx*i;
    printf("%.3lf\n",ans);
    return 0;
}

 

上一篇:ES源码之路(一):源码本地编译启动


下一篇:线性回归的推广