矩陣乘法(五):置換


      矩陣乘法在一些置換問題上有着很好的應用,特別置換次數較多時,采用矩陣快速冪運算可以加快運算過程。

      任意一個置換都能夠表示成矩陣的形式。比如,將序列1  2  3  4 置換為 3  1  2  4,相當於以下的矩陣乘法:

          

      一般來說,對於序列1, 2, ..., n,若給出置換方法a1,a2,...,an ,該置換方法表示將原序列的第pi位置上的元素換到第i位置上。

        則可以構造置換矩陣P為: P[ai][i]=1 (1<=i<=n), 其余元素全為0。

        顯然,置換矩陣每行只有一個元素為1,其余為0。

        另外,構造的置換矩陣都是可逆的,並且它的逆矩陣等於它的轉置矩陣。

【例1】解碼字符串。

      給定n個數,代表一個置換。一個長度為n的字符串s經過m次置換后變成另一個字符串t。

      例如,輸入5個數:2  3  1  5  4 代表一個置換操作。字符串s為“hello”,經過3次置換操作

"hello"  ->  "elhol"  ->  "lhelo"  ->  "helol"后,可得到字符串t為“helol”。

      輸入n、m和結果字符串t,輸出轉換前的原字符串s。

      (1)編程思路。

       由置換規則  2 3 1 5 4,可以快速構造用於字符串s轉換到t的置換矩陣P。

   

 

       而從字符串t轉換到s顯然是逆操作,而置換矩陣的逆矩陣就是其轉置矩陣。因此,用於本題的置換矩陣PT應為:    

     

 

      構造好置換矩陣后,m次操作就是置換矩陣的m次冪,之后再乘以初始序列{1 2 3 4 ....n},然后輸出相應位置的字符就可以了。

      (2)源程序。

#include <stdio.h>
#include <string.h>
struct Matrix
{
      int mat[81][81]; // 存儲矩陣中各元素
};
Matrix matMul(Matrix a ,Matrix b,int n)
{
      Matrix c;
      memset(c.mat,0,sizeof(c.mat));
      int i,j,k;
      for (k = 1; k<=n ; k++)
           for (i=1 ;i<=n ; i++)
               if (a.mat[i][k]!=0)
                   for (j = 1 ;j<=n ;j++)
                        c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) ;
      return c;
}
Matrix quickMatPow(Matrix a ,int n,int b) // n階矩陣a快速b次冪
{
      Matrix c;
      memset(c.mat ,0 ,sizeof(c.mat));
      int i;
      for (i = 1 ;i <= n ;i++)
            c.mat[i][i] = 1;
      while (b!=0)
      {
           if (b & 1)
                c = matMul(c ,a ,n); // c=c*a;
           a = matMul(a ,a ,n); // a=a*a
           b /= 2;
      }
      return c;
}
int main()
{
      int n,m,p[81],i;
      Matrix a,b,ans;
      char str[82];
      while (scanf("%d%d",&n,&m) && n!=0 && m!=0)
      {
            memset(b.mat,0,sizeof(b.mat));
            for (i=1;i<=n;i++)
                 b.mat[1][i]=i;
            memset(a.mat,0,sizeof(a.mat));
            for (i=1;i<=n;i++)
            {
                  scanf("%d",&p[i]);
                  a.mat[i][p[i]]=1;
            }
            getchar();
            gets(str);
            ans=quickMatPow(a,n,m);
            ans=matMul(b,ans,n);
            for (i=1;i<=n;i++)
                  printf("%c",str[ans.mat[1][i]-1]);
            printf("\n");
      }
      return 0;
}

      將此源程序提交給HDU 2371 “Decode the Strings”,可以Accepted。

【例2】送給聖誕夜的禮品。

      已知序列1,2,3,…,n,給出m個置換操作, 例如某個置換操作 6 1 3 7 5 2 4,表示把6位置上的元素換到1位置上,1位置上的元素換到2位置上…。    

      求原序列為1,2,3,……,n的序列按給出的m個置換操作的順序進行k次置換后得到的新序列。若k>m,則第m+1次置換操作做第1個置換操作,第m+2次置換操作做第2個置換操作,…。

     數據說明: 1<=n<=100;1<=m<=10;1<=k<=2^31-1。

      本題完整的描述可以參看 https://vijos.org/p/1049

      (1)編程思路。

     搞懂了例1,本題就容易入手了。m 個置換操作需要構造m個置換矩陣。

     構造好置換矩陣后,先將m個置換矩陣乘起來,得到ans矩陣,則此時的ans矩陣相當進行了m次操作;再將ans矩陣進行k/m次冪,此時相當進行了k次操作。當然,由於k不一定整除m,因此還需按m個置換操作的順序進行k%m次的置換操作。

      (2)源程序。

#include <stdio.h>
#include <string.h>
struct Matrix
{
      int mat[101][101]; // 存儲矩陣中各元素
};
Matrix p[11];
Matrix matMul(Matrix a ,Matrix b,int n)
{
      Matrix c;
      memset(c.mat,0,sizeof(c.mat));
      int i,j,k;
      for (k = 1; k<=n ; k++)
          for (i=1 ;i<=n ; i++)
               if (a.mat[i][k]!=0)
                  for (j = 1 ;j<=n ;j++)
                      c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) ;
      return c;
}
Matrix quickMatPow(Matrix a ,int n,int b) // n階矩陣a快速b次冪
{
      Matrix c;
      memset(c.mat ,0 ,sizeof(c.mat));
      int i;
      for (i = 1 ;i <= n ;i++)
          c.mat[i][i] = 1;
       while (b!=0)
      {
           if (b & 1)
               c = matMul(c ,a ,n); // c=c*a;
           a = matMul(a ,a ,n); // a=a*a
           b /= 2;
      }
      return c;
}
int main()
{
      int n,m,k,i,j,num,a[101];
      Matrix ans;
      scanf("%d%d%d",&n,&m,&k);
      for (i=0;i<m;i++)
      {
           memset(p[i].mat,0,sizeof(p[i].mat));
           for (j=1;j<=n;j++)
           {
                 scanf("%d",&num);
                 p[i].mat[j][num]=1;         // 構造的置換矩陣
           }
      }
      memset(ans.mat,0,sizeof(ans.mat));
      for (i=1;i<=n;i++)
            ans.mat[i][i]=1;
      for (i=0;i<m;i++)
            ans=matMul(p[i],ans,n);      // m個置換矩陣先乘起來,注意是左乘
      ans=quickMatPow(ans,n,k/m);
      for (i=0;i<k%m;i++)
            ans=matMul(p[i],ans,n);    // 剩余的k%m個矩陣相乘,代表剩余的k%m次操作
      memset(a,0,sizeof(a));
      for (i=1;i<=n;i++)
          for (j=1;j<=n;j++)
               a[i]=a[i]+(ans.mat[i][j])*j;
      for (i=1;i<=n;i++)
            printf("%d ",a[i]);
      printf("\n");
      return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM