Snakes and Ladders LightOJ - 1151 复杂期望,高斯全主元消去法解方程 [模板] —— 标程精度不够,你信我啊

11 篇文章 0 订阅
3 篇文章 0 订阅

博客目录

返回二级目录——kaugnbin概率dp习题集

原题

题目链接

'Snakes and Ladders' or 'Shap-Ludu' is a game commonly played in Bangladesh. The game is so common that it would be tough to find a person who hasn't played it. But those who haven't played it (unlucky of course!) the rules are as follows.

                                                                     

1.      There is a 10 x 10 board containing some cells numbered from 1 to 100.

2.      You start at position 1.

3.      Each time you throw a perfect dice containing numbers 1 to 6.

4.      There are some snakes and some ladders in the board. Ladders will take you up from one cell to another. Snakes will take you down.

5.      If you reach a cell that contains the bottom part of a ladder, you will immediately move to the cell which contains the upper side of that ladder. Similarly if you reach a cell that has a snake-head you immediately go down to the cell where the tail of that snake ends.

6.      The board is designed so that from any cell you can jump at most once. (For example there is a snake from 62 to 19, assume that another is from 19 to 2. So, if you reach 62, you will first jump to 19, you will jump to 2. These kinds of cases will not be given)

7.      There is no snake head in the 100-th cell and no ladder (bottom part) in the first cell.

8.      If you reach cell 100, the game ends. But if you have to go outside the board in any time your move will be lost. That means you will not take that move and you have to throw the dice again.

Now given a board, you have to find the expected number of times you need to throw the dice to win the game. The cases will be given such that a result will be found.

Input

Input starts with an integer T (≤ 105), denoting the number of test cases.

The first line of a case is a blank line. The next line gives you an integer n denoting the number of snakes and ladders. Each of the next n lines contain two integers a and b (1 ≤ a, b ≤ 100, a ≠ b). If a < b, it means that there is a ladder which takes you from a to b. If a > b, it means that there is a snake which takes you from a to b. Assume that the given board follows the above restrictions.

Output

For each case of input, print the case number and the expected number of times you need to throw the dice. Errors less than 10-6 will be ignored.

Sample Input

2

 

14

4 42

9 30

16 8

14 77

32 12

37 58

47 26

48 73

62 19

70 89

71 67

80 98

87 24

96 76

 

0

Sample Output

Case 1: 31.54880806

Case 2: 33.0476190476

题目大意

题目可看成一个1*100的条形格子,现在你站在第1个格子,格子中有许多传送点(起末点均在1到100之内),有些传送点可以向前传送,有些可以向后传送。但是传送保证不会传送到自己,传送目标点也不会处于另一个传送的起始点,st.不会连续传送。每次投掷一个均匀的骰子(1~6),骰子投出的点数就是向前走的步数,如果目标点大于100则重新投掷,求走到100格子所需投掷骰子次数的期望。(题目保证有解)

思路

设ei代表从第i个格子走到第100个格子所需期望数,显然e100=0,所以从后往前推。

对于普通的点(没有传送门的点),参照常规求解方法列状态转移方程:

e[i]=e[i+1]/6+e[i+2]/6+....+e[i+6]/6 + 1  (当然要另外考虑100-i<6的特殊情况,这里推导通式暂不考虑)

常数移到一边,转化为AX=B的形式:

ei - ei+1/6  - ei+2/6 -... -ei+6/6   =1

而对于向前传送的格子,假设起点为a,终点为a+x,有

e[a]=e[b]  ,无后效性

对于向前传送的格子,假设起点为a,终点为a-x,有

e[a]=e[b]  ,不满足无后效性,要e[a],需要知道e[a-x],所以不能递推求解

向这种存在环的问题,我们考虑用伟大的方程求解。而这里变量较多,就变成了方程组,考虑用高斯消元法来做。

然后根据以上三个公式,可列100*100系数矩阵,构造增广矩阵,高斯消元后就可以求解出e1,解毕。

PS:学过《数值分析》的童鞋知道规避误差的一个原则:防止分母过小

对于高斯消元法,有一步是需要整行除以主对角线元素的,如果主对角线元素过小则误差会很大。解决方案是将右下矩阵中最大的元素通过行交换和列交换来换掉主对角线元素(当然变量顺序也跟着列交换而改变)。

遗憾的是,出题人显然没学过《数值分析》,我用这个降低误差的方案交上去反而是错的,wa了我一晚上,睡前躺床上突然想到是不是标程误差太大了......第二天删掉降低误差的步骤就过了。好气哦......

AC代码

#include<bits/stdc++.h>
#include<cstring>
#define regi register int 
using namespace std;
//https://vjudge.net/problem/LightOJ-1151
typedef double dtype;
struct matRes{
    int i;
    dtype x;
};
inline int operator<(matRes a,matRes b){
    return a.i<b.i;
}
dtype const eps=0;

class mat{
#define accuracy 0 //1 是高精度 0是低精度  本题低精度可过不要用高精度,出题者不知道高斯消元降低误差的准则。 
#define maxsize 110 
dtype m[maxsize][maxsize];
matRes r[maxsize]; //高斯倒换临时数组 
int sw[maxsize];
dtype res[maxsize];
int rank;
public:
    int w,h;
    inline dtype *operator[](int x){
        return m[x];
    }
    inline void clear(){
        memset(m,0,sizeof(m));
    }
    inline void set_rank(int r){//方阵 + [b]
        h=r;
        w=r+1;
        for(regi i=1;i<=w;i++)
            sw[i]=i;
    }
    mat(){
        //用的时候要首先规定大小set size或者rank 
    }
    mat(int hh,int ww,dtype *a){
        regi i,j;
        w=ww;
        h=hh;
        for(i=1;i<=h;i++)
            for(j=1;j<=w;j++)
                m[i][j]=*(a+(i*w)+j);
        for(i=1;i<=w;i++)
            sw[i-1]=i-1;
    }
    
    inline void row_times(int r,dtype t){
        for(regi i=1;i<=w;i++)
            m[r][i]*=t;
    }
    inline void add_row(int r1,dtype t1,int r2){
        dtype t;
        for(regi i=1;i<=w;i++)
        {
            t=m[r1][i]*t1+m[r2][i];
            m[r2][i]=t;
        }
    }
    inline void swap_row(int r1,int r2){
        for(regi i=1;i<=w;i++)
            swap(m[r1][i],m[r2][i]);
    }
    inline void swap_col(int c1,int c2){
        for(regi i=1;i<=h;i++){
            swap(m[i][c1],m[i][c2]);
            swap(sw[c1],sw[c2]);
        }
    }
    inline void print_debug()//调试用 
    {
        #ifndef ONLINE_JUDGE 
        cout<<"变量顺序为:"<<endl;
        for(int i=1;i<w;i++)
            cout<<sw[i]<<' ';
        cout<<endl;
        cout<<"矩阵为:"<<endl;
        for(int i=1;i<=h;i++)
        {
            for(int j=1;j<=w;j++)
            {
                if(fabs(m[i][j])<eps)
                    m[i][j]=0;
                cout<<m[i][j]<<" ";
            }
            cout<<endl;
        }
        cout<<endl;
        #endif
    }
    inline void print_res(dtype dp[],int base=0){
        for(regi i=1;i<w;i++)
            dp[sw[i]+base]=res[i];
        dp[w]=dp[base];
    }

    void Gaussqzy(){
        rank=0;
        int maxi,maxj;
        dtype maxv=0;
        for(regi i=1;i<h;i++){
            maxv=0;
            for(regi j=i;j<=h;j++){
                #if accuracy == 1 //高精度 
                for(regi k=i;k<w;k++){
                    if(fabs(maxv)<fabs(m[j][k])){
                        maxv=m[j][k];
                        maxi=j;
                        maxj=k;
                    }
                } 
                #else
                if(maxv<fabs(m[j][i])){
                    maxv=fabs(m[j][i]);
                    maxi=j;
                }
                #endif
            }
            if(fabs(maxv)<eps)
                return;
            rank++;
            if(maxi!=i)
                swap_row(i,maxi);
            #if accuracy == 1 //高精度 
            if(maxj!=i)
                swap_col(i,maxj);
            #endif 
            row_times(i,1/m[i][i]);
            for(regi j=i+1;j<h;j++)
                add_row(i,-1*m[j][i],j);
            
        }
        if(fabs(m[h][h])>eps)
            rank++;
         return;
    }
    inline bool find_res(){
        Gaussqzy();
        if(rank<h)
            return 0;//不满秩 
        for(regi i=1;i<w;i++){
            res[w-1-i]=m[h-i][w];
            for(regi j=1;j<i;j++)
                res[w-1-i]-=res[w-1-j]*m[h-i][w-1-j];
        }
        for(regi i=1;i<w;i++){
            r[i].i=sw[i];
            r[i].x=res[i];
        }
        sort(r+1,r+w);
        for(regi i=1;i<w;i++){
            sw[i]=r[i].i;
            res[i]=r[i].x;
        }
        return 1;
    }
    
    
};

dtype dp[1000];
struct inp{
    int l,r;
};
inp co[1000];
bool operator<(inp a,inp b){
    return a.l<b.l;
}
int main(){
    #ifndef ONLINE_JUDGE 
    freopen("r.txt","r",stdin) ;
    #endif
    mat a;
    a.set_rank(100);
    int T,n;
    int cs=1;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(regi i=1;i<=n;i++){
            scanf("%d%d",&co[i].l,&co[i].r);
//            if(co[i].l==100){ //不知道为什么不需要 
//                i--;
//                n--;
//            }
        }
        //构建初始矩阵 
        a.clear();
        int cnt;
        for(regi i=1;i<=100;i++){
            a[i][i]=1;
            cnt=6;
            for(regi j=i+1;j<=100 && j<=i+6;j++){
                a[i][j]=-1.0/6;
                cnt--;
            }
            a[i][i]-=1.0*cnt/6;
            a[i][101]=1;
        }
        a[100][100]=1;
        a[100][101]=0;//dp[100+1]=0; 清掉常数,dp[l]=dp[r] 
        //梯子 蛇
        for(regi i=1;i<=n;i++){
            a.row_times(co[i].l,0);//行清零 
            a[co[i].l][co[i].r]=-1;
            a[co[i].l][co[i].l]=1;
        }
        a.find_res();
        a.print_res(dp);
    //    a.print_debug();
        printf("Case %d: %.8f\n",cs++,dp[1]);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值