BNU29139——PvZ once again——————【矩阵快速幂】

PvZ once again

Time Limit: 2000ms
Memory Limit: 65536KB
64-bit integer IO format:  %lld      Java class name: Main
Type: 
None
 
 

植物大战僵尸算个out的游戏了,原谅被出题逼疯了的跑来挖坟了。

会玩的请无视这一段直接看题目{

游戏中僵尸向你的房子进发,吃掉沿途遇到的植物进入你的房子 你就死翘了

你在土地上种植各种植物来攻击阻挡僵尸

手推车:放置在终点,僵尸走到面前会启动推倒一整行的僵尸

大蒜:可种植的一种植物,发出恶心的气味,僵尸咬了一口就会换到邻近的另一行(如果有相邻两行,那么移动到另外两行概率是相等的)

南瓜:单纯的肉盾 被僵尸啃的

耐久度K: 植物被咬了K口后被僵尸吃掉

如有其他对游戏的不理解请clarify

}

问题是这样的:

我们的院子变成了N行M列的,而且种满了大蒜(耐久度K)(图是我盗了 我不会这么无聊的)coming的僵尸只有一只(然而这只僵尸貌似发生了变异,它每啃一口植物,同一列相同种类的植物也被啃掉一口,一口一排的样子恩恩),初始位置在第S行,因为没有放置攻击性的植物,所以僵尸就一路吃了,于是出题者很想知道僵尸死在自上而下1-N号手推车的概率各是多少

BNU29139——PvZ once again——————【矩阵快速幂】_第1张图片

(无视掉图中的南瓜,实际上对僵尸行走没有影响。。)

 

Input

一个整数T(表示T组数据)

接下来的T组数据

每组给定四个整数 N M K S

数据范围

T<=1000

0<N<=20

0<M<=1000

0<K<=1000

1<=S<=N

 

Output

对于每组数据输出一行N个4位小数 用空格隔开 表示僵尸死在相应行的概率 行末没有空格

 

Sample Input

1
5 9 5 3

Sample Output

0.0000 0.5000 0.0000 0.5000 0.0000

Source

 
 
解题思路:求出概率转移矩阵,因为要转移n*k+1次,即(原始概率矩阵B)*(概率转移矩阵A) n*k,所以根据矩阵相乘的结合律,所以可以让概率转移矩阵A先乘n*k次,因为B矩阵为 所以最后只需输出转移n*k次后的矩阵的第s行即为所求。
 
 
 
#include<bits/stdc++.h>
using namespace std;
int n,m,k,s;
struct Matrix{
    double a[25][25];
    void init(){
        memset(a,0,sizeof(a));
    }
    void unit(){
        for(int i=1;i<=n;i++){
            a[i][i]=1.0000;
        }
    }
    Matrix operator * (Matrix &X)const {
        Matrix ret;
        ret.init();
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                    ret.a[i][j]=0.0;
                for(int k=1;k<=n;k++){
                    ret.a[i][j]+= X.a[i][k] * a[k][j];
                }
            }
        }
        return ret;
    }
}A,B;
void debug(){

    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            printf("%.3lf ",B.a[i][j]);
                printf("\n");
    }
}
Matrix &POW(Matrix &tB,int x){
    while(x){
        if(x&1){
            tB=tB*A;
        }
        x>>=1;
        A=A*A;
    }
    return tB;
}
int main(){

    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d%d",&n,&m,&k,&s);
        if(n==1){

            printf("1.0000\n");
            continue;
        }else{
            A.init();
            for(int i=1;i<=n;i++){
                if(i==1){
                    A.a[i][2]=1.0;
                }else if(i==n){
                    A.a[i][n-1]=1.0;
                }else {
                    A.a[i][i+1]=A.a[i][i-1]=0.5;
                }
            }
            B.init();
            B.unit();
           // debug();
            POW(B,m*k);
            for(int i=1;i<=n;i++){
                printf("%.4lf%c",B.a[s][i],i==n?'\n':' ');
            }
        }
    }
    return 0;
}

  

 

图文详解:

假设以n,m,k,s分别为5,9,9,3为例。BNU29139——PvZ once again——————【矩阵快速幂】_第2张图片P、PP、PPP分别代表咬1、2、3口后的概率,只是对于耐久度为9时,他们是在同一列的,只不过为了表示,所以这样给出,不要误解。PP1=0*  P1+0.5*  P2+0*   P3+0*  P4+  0 * P5

                PP2=1*  P1+0 *    P2+0.5*P3+0*  P4+0*    P5

                PP3=0*  P1+0.5*  P2+0*   P3+0.5*P4+0*   P5

                PP4=0*  P1+0*    P2+0.5* P3+0* P4+1*   P5

                PP5=0*  P1+0*    P2+0*    P3+0.5*P4+0*   P5

由上表可以看出,后一个列概率矩阵PP由前一个概率矩阵P乘以某一个矩阵得到。我们假设该某矩阵为A即:


0

0

0.5

0

0.5

0

0

0

0.5

0

0.5

0

0

0

0.5

0

0.5

0

0

0

1

0

将列概率矩阵转为行概率矩阵B:

P1

P2

P3

P4

P5


用B*A得到下一个行概率矩阵B‘。同时由于矩相乘具有结合律,所以我们可以用矩阵快速幂来先求出转移n次的转移矩阵An*k,然后用原始矩阵B*An*k即可求得。由于原始矩阵B只有第s列为1,所以只需最后将矩阵的s行1---n列顺序输出即为答案。

 

 

你可能感兴趣的:(in)