HDU-4936 Rainbow Island(期望dp+高斯消元+hash)

来源:互联网 发布:淘宝商家版登录 编辑:程序博客网 时间:2024/06/07 00:51

Rainbow Island

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 87    Accepted Submission(s): 33


Problem Description
Recently, a new emerging game has become popular in Hogwarts.

The goal of the game is to connect all islands in the ground with rainbows. At the beginning, n islands are isolated and the player arrive at the first island. There is a red magic set and a blue magic set on each island. Once the players of the game arrive at one of the islands, they will follow the sequence, which is firstly entering the red magic set, then the blue one. When a player enters the red magic set, he/she has the possibility of p to gain a rainbow, thereby two random island of all will be connected by the rainbow(including which two have been connected). When a player enters the blue magic set, he/she will be sent to one of the s islands with same possibility. One unit of total magic volume will be expended when the player uses the blue magic. At the end of the game, the winner is the one who consumes the least of total magic volume.

However, the brilliant Hermione despises the game, she wants to know the expected magic volume to be taken away the goal of the game was achieved.
 

Input
There are multiply test cases.

The first line contains an integer T(T<=100), indicates the number of cases.
For each case the first line contains an integer n, indicates the number of the islands. (1<=n<=20)

Then there will be n numbers, pi indicates the possibility to gain a rainbow for the i-th island. (0<pi<1)

The next n lines, each line firstly contains an integer s, then followed by s integers, indicates the number of which the i-th island can arrive at. (1 <= s <= n)
 

Output
For each test case, you should output “Case #K: “ first, where K indicates the case number and counts from 1. Then output the answer. Round the answer to the sixth digit after the decimal point.
 

Sample Input
230.123 0.984 0.632 1 22 2 32 1 320.75 0.342 1 22 1 2
 

Sample Output
Case #1: 4.931167Case #2: 1.458716
Hint
If the player achieve the goal of the game, he/she won't enter the blue magic set anymore.

题意:有n座岛屿,玩家一开始在1号岛屿,每个岛屿有2种魔法阵,
每到一个岛屿玩家先进入红色魔法阵,然后进入蓝色魔法阵
①红色魔法阵:第i个岛屿有p[i]的概率召唤一道彩虹将随机的两座岛屿连接起来(已经被连接的仍可能被选)
②蓝色魔法阵:随机传送到s个岛屿中的其中一个,进入蓝色魔法阵会消耗1单位魔法
问:将n个岛屿连接起来所要消耗的魔法的期望值(如果完成了任务则不再进入蓝色魔法阵)


题解:期望DP+高斯消元+hash
定义状态:当前有多少个联通块,联通块的大小为多少,这里显然可以用hash来存状态,当n=20时,状态数不超过700

定义dp[i][j]:当前状态为i,玩家在第j个岛屿上时完成任务的魔法的期望
因为一个状态的变化必然导致联通块减少,于是状态转移是单向的。
设当前状态为i,当前在第j个岛屿,下一个状态为k,下一个岛屿为v,如果状态转移失败,则i==k
①状态转移成功(即联通块减少了):假设合并的2个联通块大小为x,y
dp[i][j]+=∑(p[j]*x*y/(n*(n-1)/2)*(dp[k][v]+1))
②状态转移失败:假设转移成功的概率为success,j可以转移的岛屿数为cnt
dp[i][j]+=∑(1-success)/cnt*(dp[i][v]+1)
第②种情况可以有高斯消元求解

/*题意:有n座岛屿,玩家一开始在1号岛屿,每个岛屿有2种魔法阵,每到一个岛屿玩家先进入红色魔法阵,然后进入蓝色魔法阵①红色魔法阵:第i个岛屿有p[i]的概率召唤一道彩虹将随机的两座岛屿连接起来(已经被连接的仍可能被选)②蓝色魔法阵:随机传送到s个岛屿中的其中一个,进入蓝色魔法阵会消耗1单位魔法问:将n个岛屿连接起来所要消耗的魔法的期望值(如果完成了任务则不再进入蓝色魔法阵)题解:概率DP+高斯消元+hash定义状态:当前有多少个联通块,联通块的大小为多少,这里显然可以用hash来存状态,当n=20时,状态数不超过700定义dp[i][j]:当前状态为i,玩家在第j个岛屿上时完成任务的魔法的期望因为一个状态的变化必然导致联通块减少,于是状态转移是单向的。设当前状态为i,当前在第j个岛屿,下一个状态为k,下一个岛屿为v,如果状态转移失败,则i==k①状态转移成功(即联通块减少了):假设合并的2个联通块大小为x,ydp[i][j]+=∑(p[j]*x*y/(n*(n-1)/2)*(dp[k][v]+1))②状态转移失败:假设转移成功的概率为success,j可以转移的岛屿数为cntdp[i][j]+=∑(1-success)/cnt*(dp[i][v]+1)第②种情况可以有高斯消元求解*/#include<bits/stdc++.h>using namespace std;const int MX = 705;const int PA = 233;const int PB = 1000007;struct State{    int s[25],tot;}sta[MX];int n,a[25][25];int s[25],sz,Hash[PB+10],to[MX][25][25];double dp[MX][25],p[25];int code(State st){    int ret=st.tot;    for(int i=0;i<st.tot;i++) ret=(ret*PA%PB+st.s[i])%PB;    return ret;}void dfs(int num,int mx,int sum){    if(sum==0){        sta[++sz].tot=num;        for(int i=0;i<num;i++) sta[sz].s[i]=s[i];        sort(sta[sz].s,sta[sz].s+num,greater<int>());        Hash[code(sta[sz])]=sz;        return;    }    for(int i=mx;i<=sum;i++) dfs(num+1,s[num]=i,sum-i);}int merge(int i,int x,int y){    State t=sta[i];    t.s[x]+=t.s[y];    t.s[y]=0;    swap(t.s[y],t.s[t.tot]);    sort(t.s,t.s+t.tot,greater<int>());    t.tot--;    return Hash[code(t)];}void pre_solve(){    sz=0;    dfs(0,1,n);    memset(to,0,sizeof(to));    memset(dp,0,sizeof(dp));    for(int i=1;i<=sz;i++){        //printf("%d",i);        for(int x=0;x<sta[i].tot;x++){            for(int y=x+1;y<sta[i].tot;y++){                to[i][x][y]=merge(i,x,y);                //printf("[%d]",to[i][x][y]);            }        }        //puts("");    }}/*变量为n个时,矩阵A大小为n*n,b为等式右边,通常将b设为0,再让A[i][i]+=-1X保存高斯消元后的结果*/const double eps = 1e-8;typedef vector<double> vec;typedef vector<vec> mat;vec gauss_jordan(const mat& A, const vec& b) {    int n = A.size();    mat B(n, vec(n + 1));    for (int i = 0; i < n; i++)        for (int j = 0; j < n; j++) B[i][j] = A[i][j];    for (int i = 0; i < n; i++) B[i][n] = b[i];    for (int i = 0; i < n; i++) {        int pivot = i;        for (int j = i; j < n; j++) {            if (fabs(B[j][i]) > fabs(B[pivot][i])) pivot = j;        }        swap(B[i], B[pivot]);        if (fabs(B[i][i]) < eps) return vec();        for (int j = i + 1; j <= n; j++) B[i][j] /= B[i][i];        for (int j = 0; j < n; j++) {            if (i != j) {                for (int k = i + 1; k <= n; k++) B[j][k] -= B[j][i] * B[i][k];            }        }    }    vec x(n);    for (int i = 0; i < n; i++) x[i] = B[i][n];    return x;}void solve(){    for(int i=sz-1;i>0;i--){        mat A(n,vec(n));vec B(n);        State t=sta[i];        for(int j=0;j<n;j++){            double q=0;            for(int x=0;x<t.tot;x++){                for(int y=x+1;y<t.tot;y++){                    int k=to[i][x][y];                    double ps=p[j]*t.s[x]*t.s[y]*2/(n*(n-1));                    q+=ps;                    for(int d=1;d<=a[j][0];d++){                        int v=a[j][d];                        dp[i][j]+=ps*dp[k][v]/a[j][0];                    }                }            }            A[j][j]=1; B[j]=dp[i][j]+1;            double fail=1-q;            for(int d=1;d<=a[j][0];d++){                int v=a[j][d];                A[j][v]-=fail/a[j][0];            }        }        B=gauss_jordan(A,B);        for(int j=0;j<n;j++) dp[i][j]=B[j];    }}int main(){    int T;    //freopen("in.txt","r",stdin);    scanf("%d",&T);    for(int cas=1;cas<=T;cas++){        scanf("%d",&n);        for(int i=0;i<n;i++) scanf("%lf",&p[i]);        for(int i=0;i<n;i++){            scanf("%d",&a[i][0]);            for(int j=1;j<=a[i][0];j++) scanf("%d",&a[i][j]),a[i][j]--;        }        pre_solve();        solve();        printf("Case #%d: %f\n",cas,dp[1][0]);    }    return 0;}



原创粉丝点击