好吧,我觉得这道题有两个难点。。。第一个是我没想到转化为矩阵的求解。。。这个应该是做的这方面题目比较少的缘故。第二个就是需要对稀疏矩阵的乘法进行优化,这个也是学到了。
在学长提示了说要用到矩阵之后我就基本上想到大概的方法了,把猫咪的花生数量看做矩阵里面的元素的话,联系线代里面的知识就可以想到给花生数量矩阵再乘上一个矩阵来进行变换。然后把k个指令对应的变换矩阵乘起来之后得到一个新矩阵,花生数量矩阵乘上这个新矩阵就直接相当于做了k次的指令了。要求重复m次,那就是给新矩阵上一个快速幂,得到一个最终的变换矩阵出来了,一切就出来了。
在矩阵变换的时候我想了很久处理花生数量+1的办法,可是依然没有找到合适的办法,最后看了一下发现是把原矩阵拓宽一层。。好吧,这也是一个好技巧。
(花生1,花生2,花生3,花生4, 1)(第k+1条指令前)=(花生1,花生2,花生3,花生4, 1)(第k条指令前)*k+1条指令的变换矩阵
好吧。。。我不会画矩阵。。。。我把我看的题解的blog放在这里吧。http://blog.csdn.net/magicnumber/article/details/6217602 这儿的矩阵画的挺好的,不过我的写法和他那儿的写法的行列恰好是反的,而且我的下标+了1。
其实最后可以发现,指令变换矩阵相乘都可以省略掉,可以通过直接修改矩阵元素得到新矩阵,然后通过快速幂直接到最终矩阵。
2016/11/11 附加:其实这样的坐标表示就是计算机图形学里面讲到的齐次坐标,在普通的笛卡尔坐标下,空间的一个点通过一个三维向量来描述,但是在齐次坐标系下用一个4维向量来描述。举个例子,假若某个点K(x,y,z),那么其齐次坐标就可以表示为(xh,yh,zh,h),其中h是一个常数。
这样表示有两个好处,①当h=0是可以表示无穷远处
②(从图形学的角度来说)可以进行平移变换,但是在笛卡尔坐标系下是不能进行平移变换的,只能缩放和旋转,对应到这道题目来说就是这样,可以进行加减了。
Tips:假若是花生数量+1,则是matrix[n+1][x]++(此处就体现了在末尾加1的妙用)
假若是花生数量清零,则对应的那一列都清零
假若是花生数量交换,则对应的两列交换
稀疏矩阵乘法加速在此题里面很有用
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
typedef ll matrix[105][105];
ll n,m,k,x,y;
matrix arr,tem,ans;
void mul(matrix,matrix);
char c;
int main(){
while(scanf("%lld%lld%lld%*c",&n,&m,&k)&&(n||m||k)){
for(int i=1;i<=n+1;++i)
arr[i][i]=ans[i][i]=1;
for(int i=1;i<=k;++i){
scanf("%c",&c);
if(c=='g'){
scanf("%lld%*c",&x);
arr[n+1][x]++;
}
else if(c=='e'){
scanf("%lld%*c",&x);
for(int i=1;i<=n+1;++i)
arr[i][x]=0;
}
else{
scanf("%lld%lld%*c",&x,&y);
for(int i=1;i<=n+1;++i)
swap(arr[i][x],arr[i][y]);
}
}
while(m){
if(m&1)
mul(ans,arr);
mul(arr,arr);
m>>=1;
}
for(int i=1;i<n;++i)
printf("%lld ",ans[n+1][i]);
printf("%lld\n",ans[n+1][n]);
memset(arr,0,sizeof arr);
memset(ans,0,sizeof ans);
}
return 0;
}
void mul(matrix a,matrix b){
for(int i=1;i<=n+1;++i)
for(int j=1;j<=n+1;++j)
if(a[i][j])
for(int k=1;k<=n+1;++k)
tem[i][k]+=a[i][j]*b[j][k];
for(int i=1;i<=n+1;++i)
for(int j=1;j<=n+1;++j)
a[i][j]=tem[i][j];
memset(tem,0,sizeof tem);
}