植物大战僵尸算个out的游戏了,原谅被出题逼疯了的跑来挖坟了。
会玩的请无视这一段直接看题目{
游戏中僵尸向你的房子进发,吃掉沿途遇到的植物进入你的房子 你就死翘了
你在土地上种植各种植物来攻击阻挡僵尸
手推车:放置在终点,僵尸走到面前会启动推倒一整行的僵尸
大蒜:可种植的一种植物,发出恶心的气味,僵尸咬了一口就会换到邻近的另一行(如果有相邻两行,那么移动到另外两行概率是相等的)
南瓜:单纯的肉盾 被僵尸啃的
耐久度K: 植物被咬了K口后被僵尸吃掉
如有其他对游戏的不理解请clarify
}
问题是这样的:
我们的院子变成了N行M列的,而且种满了大蒜(耐久度K)(图是我盗了 我不会这么无聊的)coming的僵尸只有一只(然而这只僵尸貌似发生了变异,它每啃一口植物,同一列相同种类的植物也被啃掉一口,一口一排的样子恩恩),初始位置在第S行,因为没有放置攻击性的植物,所以僵尸就一路吃了,于是出题者很想知道僵尸死在自上而下1-N号手推车的概率各是多少
一个整数T(表示T组数据)
接下来的T组数据
每组给定四个整数 N M K S
数据范围
T<=1000
0<N<=20
0<M<=1000
0<K<=1000
1<=S<=N
问题到这就转化为求这个转移矩阵;一开始我用DP的方法,由1次递推到k次,考虑时间复杂度,有n个起始位置,k次转移,有n行,有T组case,总复杂度为1000*1000*20*20,超时了。。。。
最终你会发现在求这个转移矩阵同样可以用 矩阵快速幂。
考虑到他转移的特殊性,他在当前行只能转移到相邻两行,同样的你可以先构造一个转移矩阵
假设n=5,转移矩阵为: 0 0.5 0 0 0 第一行只能由第二行转移过来并且概率为0.5
1 0 0.5 0 0 第二行可由第一行转移过来,概率为1,也可由第三行转移过来,概率为0.5
0 0.5 0 0.5 0
0 0 0.5 0 1
0 0 0 0.5 0
然后初始化起点矩阵,运用k次快速幂,就可以求得M[i][j]了;
不过,还要注意当n为1的时候,直接输出概率为1.。。。。。(被卡了半小时。。)
代码奉上:
#include <cstdio> #include <cstring> using namespace std; int n, m, k, s; struct M { double a[21][21]; }b, res, tmp, tmp2; M multiply(M x, M y) { M temp; for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++)temp.a[i][j]=0; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { for(int l=1;l<=n;l++) { temp.a[i][j]+=x.a[i][l]*y.a[l][j]; } } } return temp; } void calc(int N) { while(N) { if(N&1) res=multiply(res,b); N>>=1; b=multiply(b,b); } } void calc2(int N) { while(N) { if(N&1) tmp2=multiply(tmp2,tmp); N>>=1; tmp=multiply(tmp,tmp); } } 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; } //第一次快速幂 memset(tmp.a,0,sizeof(tmp.a)); //初始化第一次的转移矩阵 for(int i = 1; i <= n; i++) { if(i-1==0) tmp.a[i][i+1]=0.5; else if(i==n) tmp.a[i][i-1]=0.5; else { if(i==2) {tmp.a[i][i+1]=0.5;tmp.a[i][i-1]=1;} else if(i==n-1) {tmp.a[i][i+1]=1;tmp.a[i][i-1]=0.5;} else {tmp.a[i][i+1]=0.5;tmp.a[i][i-1]=0.5;} } } memset(tmp2.a,0,sizeof(tmp2.a)); for(int i =1; i <= n; i++)tmp2.a[i][i]=1.0; calc2(k); for(int i = 1; i <= n; i++)//求解第二次的转移矩阵 { double h[21],h2[21]; memset(h,0,sizeof(h)); memset(h2,0,sizeof(h2)); h[i]=1.0; for(int j = 1; j <= n; j++) { for(int l = 1; l <= n; l++) { h2[j]+=tmp2.a[j][l]*h[l]; } } for(int j = 1; j <= n; j++) b.a[j][i]=h2[j]; } //第二次快速幂 double start[21], ans[21]; memset(start, 0, sizeof(start)); start[s]=1; for(int i = 1; i <= n; i++)for(int j = 1; j <= n; j++)res.a[i][j]=0; for(int i = 1; i <= n; i++)res.a[i][i]=1.0; calc(m); for(int i = 1; i <= n; i++) { ans[i]=0; for(int j = 1; j <= n; j++) { ans[i]+=res.a[i][j]*start[j]; } } int flag=0; for(int i = 1; i <= n; i++) { if(!flag) { printf("%.4f",ans[i]); flag=1; } else printf(" %.4f",ans[i]); } printf("\n"); } return 0; }
#include <cstdlib> #include <cstring> #include <cstdio> #include <iostream> using namespace std; #define N 1000 int k,n;//k次,n为数组大小 struct M { int a[N][N]; }origin,res; M multiply(M x,M y) { M temp; memset(temp.a,0,sizeof(temp.a)); for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { for(int k=0;k<n;k++) { temp.a[i][j]+=x.a[i][k]*y.a[k][j]; } } } return temp; } void init() { memset(res.a,0,sizeof(res.a)); for(int i = 0; i < n; i++)res.a[i][i]=1;//将res.a初始化为单位矩阵 } void calc(int m) { while(m) { if(m&1) res=multiply(res,origin); m>>=1; origin=multiply(origin,origin); } } int main() { while(cin>>n) { init(); calc(k); } return 0; }