bzoj 2337 高斯消元+概率DP

  题目大意:

每条路径上有一个距离值,从1走到N可以得到一个所有经过路径的异或和,求这个异或和的数学期望

 

这道题直接去求数学期望的DP会导致很难列出多元方程组

我们可以考虑每一个二进制位从1走到N的平均概率值

因为整个图是联通的那么所有点都默认会处于多元方程组中

Pi = p[i] * sigma( v&d[i][j]?(1-Pj):Pj)

v是当前二进制位代表的数值

这里需要注意的是自环的加边情况,自环只加一次边,不能向平时那样加无向边一样

  1 #include <cstdio>

  2 #include <cstring>

  3 #include <iostream>

  4 #include <algorithm>

  5 #include <cmath>

  6 #include <queue>

  7 using namespace std;

  8 const int N = 205;

  9 const int M = 10005;

 10 #define eps 1e-8

 11 int first[N] , k , degree[N] , n , m;

 12 double p[N] , a[N][N];

 13 

 14 void debug()

 15 {

 16     for(int i=0 ; i<n ; i++){

 17         for(int j=0 ; j<=n ; j++)

 18             cout<<a[i][j]<<" ";

 19         cout<<endl;

 20     }

 21 }

 22 

 23 struct Edge{

 24     int y , next , d;

 25     Edge(int y=0 , int next=0 , int d=0):y(y),next(next),d(d){}

 26 }e[M<<1];

 27 

 28 void add_edge(int x,int y,int d)

 29 {

 30     e[k] = Edge(y , first[x] , d);

 31     first[x] = k++;

 32 }

 33 

 34 int get_width(int x)

 35 {

 36     int ret =0 ;

 37     while(x)

 38     {

 39         x>>=1;

 40         ret++;

 41     }

 42     return ret;

 43 }

 44 

 45 void build(int v)

 46 {

 47     memset(a , 0 , sizeof(a));

 48     for(int i=0 ; i<n ; i++){

 49         a[i][i] = 1;

 50         if(i==n-1) continue;

 51         for(int j=first[i] ; ~j ; j=e[j].next){

 52             int u = e[j].y;

 53             if(v&e[j].d){

 54                 a[i][u] += p[i];

 55                 a[i][n] += p[i];

 56             }else{

 57                 a[i][u] -= p[i];

 58             }

 59         }

 60     }

 61    // debug();

 62 }

 63 

 64 int gauss(int n)

 65 {

 66     int i,j,k;

 67     for(i=0 , j=0 ; i<n&&j<n ; j++){

 68         for(k=i ; k<n ; k++)

 69             if(fabs(a[k][j])>eps) break;

 70         if(k<n){

 71             if(i!=k)

 72                 for(int r=j ; r<=n ; r++) swap(a[i][r],a[k][r]);

 73             double tt = 1.0/a[i][j];

 74             for(int r=j ; r<=n ; r++) a[i][r] *= tt;

 75             for(int r=0 ; r<n ; r++){

 76                 if(r == i) continue;

 77                 for(int t=n ; t>=j ; t--)

 78                     a[r][t] -= a[i][t]*a[r][j];

 79             }

 80             i++;

 81         }

 82     }

 83     for(int r=i ; r<n ; r++)

 84         if(fabs(a[r][n])>eps) return 0;

 85     return 1;

 86 }

 87 

 88 int main()

 89 {

 90     #ifndef ONLINE_JUDGE

 91         freopen("a.in" , "r" , stdin);

 92     #endif // ONLINE_JUDGE

 93     while(~scanf("%d%d" , &n , &m))

 94     {

 95         memset(first , -1 , sizeof(first));

 96         memset(degree , 0 , sizeof(degree));

 97         k = 0;

 98         int maxn = 0;

 99         for(int i=0 ; i<m ; i++){

100             int x,y,d;

101             scanf("%d%d%d" , &x , &y , &d);

102             add_edge(x-1,y-1,d);

103             if(x!=y){

104                 add_edge(y-1 , x-1 , d);

105                 degree[x-1]++ , degree[y-1]++;

106             }

107             else degree[x-1]++;

108             maxn = max(maxn , d);

109         }

110         for(int i=0 ; i<n ; i++) p[i] = 1.0/(degree[i]*1.0);//得到从当前点每条边出发的概率

111         int len = get_width(maxn);

112         double ret= 0;

113         for(int i=0 ; i<len ; i++){

114             int v = 1<<i;

115             build(v);

116             gauss(n);

117             ret += v*a[0][n];

118         }

119         printf("%.3f\n" , ret);

120     }

121     return 0;

122 }

 

你可能感兴趣的:(ZOJ)