矩陣乘法(七):其它一些典型應用

来源:https://www.cnblogs.com/cs-whut/archive/2019/09/07/11479080.html
-Advertisement-
Play Games

前面幾篇隨筆中介紹了利用矩陣乘法(特別是應用快速冪運算)解決遞推快速求值、置換和幾何變換等問題的方法。實際上矩陣乘法的應用遠不止這些,下麵通過幾個實例來介紹下矩陣乘法的其它一些典型的應用。 【例1】多少條道。 給定一個有向圖,問從A點恰好走k步(允許重覆經過邊)到達B點的方案數mod p的值。 (1 ...


      前面幾篇隨筆中介紹了利用矩陣乘法(特別是應用快速冪運算)解決遞推快速求值、置換和幾何變換等問題的方法。實際上矩陣乘法的應用遠不止這些,下麵通過幾個實例來介紹下矩陣乘法的其它一些典型的應用。

【例1】多少條道。

      給定一個有向圖,問從A點恰好走k步(允許重覆經過邊)到達B點的方案數mod p的值。

      (1)編程思路。

      本題是矩陣乘法應用在圖論中的一個典型方法。
      給定了有向圖,可以得到該圖的鄰接矩陣A,在鄰接矩陣A中,A(i,j)=1當且僅當存在一條邊i->j。若i->j不存在直接相連接的邊,則A(i,j)=0。

      令C=A*A,那麼 C(i,j)= ΣA(i,k)*A(k,j),實際上就等於從點i到點j恰好經過2條邊的路徑數(k為中轉點)。

      類似地,C*A =A*A*A的第i行第j列就表示從i到j經過3條邊的路徑數。同理,如果要求經過k步的路徑數,只需要採用快速冪運算求出A^k即可。

      (2)源程式。

#include <stdio.h>
#include <string.h>
#define MOD 1000
struct Matrix
{
      int mat[21][21]; // 存儲矩陣中各元素
};
Matrix matMul(Matrix a ,Matrix b,int n,int m)
{
      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]) % m;
      return c;
}
Matrix quickMatPow(Matrix a ,int n,int b,int m) // 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,m); // c=c*a;
            a = matMul(a ,a ,n,m); // a=a*a
            b /= 2;
      }
      return c;
}
int main()
{
      int n,m,s,t,nCase,a,b,k,i;
      Matrix p,ans;
      while (scanf("%d%d",&n,&m) && n+m!=0)
      {
            memset(p.mat,0,sizeof(p.mat));
            for (i=1;i<=m;i++)
            {
                 scanf("%d%d",&s,&t);
                 p.mat[s+1][t+1]=1;
            }
            scanf("%d",&nCase);
            for (i=1;i<=nCase;i++)
            {
                  scanf("%d%d%d",&a,&b,&k);
                  ans=quickMatPow(p,n,k,MOD);
                  printf("%d\n" ,ans.mat[a+1][b+1]);
             }
      }
      return 0;
}

將此源程式提交給 HDU 2157 “How many ways??”,可以Accepted。

 

       我們知道,構造好平移、縮放或旋轉的轉換矩陣後,可以實現幾何變換;構造好置換矩陣後,可以實現置換操作。這樣,在一些問題中,我們也可以根據狀態變化的情況,構造一個狀態轉移矩陣,來解決一些狀態變換類問題。

【例2】燈的狀態。

     有n盞燈排成一排,開關狀態已知,0代表燈熄滅,1代表點亮。每過一秒:第i(1<=i<=n)盞燈會根據剛纔左邊的那個燈的開關情況變化,如果左邊的燈是亮的,它就會變化,如果左邊的燈是熄滅的,它就保持原來狀態。第1盞燈的左邊是最後一盞燈。問m秒後全部n盞燈的狀態。

      (1)編程思路。

        設f[i]代表第i盞燈的狀態,f[i]=1代表第i盞燈是點亮的,f[i]=0代表第i盞燈是熄滅的。

        對於第i(1<i<=n)盞燈,若第i-1盞燈點亮的: 當前燈的動作:   1->0;  0->1;

                                               若第i-1盞燈熄滅的: 當前燈的動作: 1->1; 0->0;

        由此,可推得  f[i]=(f[i]+f[i-1])%2  (1<i<=n)。 

       對於第1盞燈,它的狀態變化與第n盞燈相關,即  f[1]=(f[1]+f[n])%2 。

       由此,我們可以構造一個n*n的狀態轉移矩陣P來完成燈的狀態轉換。

                      

                             

         構造好狀態轉移矩陣P,P^m的結果就是m秒後的狀態轉移矩陣。再將狀態轉移矩陣除以n盞燈初始狀態列向量F即可得到n盞燈的最終狀態。

      (2)源程式。

#include <stdio.h>
#include <string.h>
#define MOD 2
struct Matrix
{
      int mat[101][101]; // 存儲矩陣中各元素
};
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]) % MOD;
     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 m,n,i,j,s;
      char f[101];
      int temp[101],ans[101];
      Matrix p;
      while(scanf("%d" ,&m)!=EOF)
       {
            scanf("%s",f);
            n=strlen(f);
            for (i=1;i<=n;i++)
                temp[i]=f[i-1]-'0';
            memset(p.mat,0,sizeof(p.mat));
            p.mat[1][1]=p.mat[1][n]=1;
            for (i=2;i<=n;i++)
                   p.mat[i][i-1]=p.mat[i][i]=1;
            p = quickMatPow(p,n,m);
            for (i=1;i<=n;i++)
             {
                   s=0;
                   for (j=1;j<=n;j++)
                        s+=p.mat[i][j]*temp[j];
                   ans[i]=s%MOD;
            }
            for (i=1;i<=n;i++)
                 printf("%d" ,ans[i]);
            printf("\n");
      }
      return 0;
}

     將此源程式提交給 HDU 2276 “Kiki & Little Kiki 2”,可以Accepted。

 【例3】訓練小貓。

      用k個操作序列來訓練n只小貓。 操作系列中:

      g  i:表示第i只貓得到一個花生, e  i :表示第i只貓吃掉所有花生,s i j :表示第i只貓和第j只貓交換花生。
      k個操作序列要被重覆m次,問最後每隻貓有多少花生。

      (1)編程思路。

       構造一個(n+1)*(n+1)(下標從1開始)的轉換矩陣P,該轉換矩陣根據K個操作序列來構造,具體構造方法是:

       先將P初始化為單位矩陣。

       對於g i操作,將當前矩陣P的第i行的第(n+1)列加1。 

       例如,有3只小貓,某次操作前3只小貓的花生數分別為x,y和z。 執行 g 2 操作。矩陣的構造表示為

              

       對於 e  i 操作:將當前矩陣P第i行全部清0即可。

      對於 s i j 操作 :交換當前矩陣P的第i行和第j行即可。

      構造好了轉換矩陣(註意該轉換矩陣代表著一次k個操作序列),執行P^m,相當將k個操作序列重覆m次。

      由於n只小貓的初始花生數均為0,因此最終P矩陣的第n+1列的第1~n行元素的值就是最後n只小貓的最後花生數。

      (2)源程式。

#include <stdio.h>
#include <string.h>
struct Matrix
{
      __int64 mat[105][105];
};
Matrix start;
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])
                      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 b ,int n) // 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)
      {
          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;
      __int64 t;
      while (scanf("%d%d%d",&n,&m,&k)!=EOF)
      {
            if (n==0 && m==0 && k==0)
                  break;
            n++;
            memset(start.mat, 0, sizeof(start.mat));
            for (i = 1; i<=n; i++)
                 start.mat[i][i] = 1;
            for (i=1; i<=k; i++)
            {
                  int a, b;
                  char ch[2];
                  scanf("%s", ch);
                  if (ch[0] == 'g')
                  {
                        scanf("%d", &a);
                        start.mat[a][n]++;
                 }
                 else if (ch[0] == 'e')
                 {
                        scanf("%d", &a);
                        for (j = 1; j <= n; j++)
                              start.mat[a][j] = 0;
                 }
                 else
                 {
                        scanf("%d%d", &a, &b);
                        for (j = 1; j<=n; j++)
                        {
                               t=start.mat[a][j];
                               start.mat[a][j]=start.mat[b][j];
                               start.mat[b][j]=t;
                        }
                }
           }
           start= quickMatPow(start,m,n);
           for (i = 1; i <n; i++)
                 printf("%I64d ", start.mat[i][n]);
           printf("\n");
      }
      return 0;
}

將此源程式提交給 POJ 3735“Training little cats”,可以Accepted。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 2 3 4 5 Document 6 13 21 22 23 24 25 ...
  • 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 <style type="text/css"> 7 #div1{ 8 width: 200px; 9 ... ...
  • 上一篇我們主要講解了函數的執行過程和原理,本篇我們將介紹函數的另外兩個特殊表現:閉包和立即執行函數。 一 閉包 1, 閉包的形成 之前我們提到,函數執行完畢,馬上就會銷毀自己的AO對象。但是如果遇到下麵這種情況:有子函數的定義,並將子函數返回。它真的就完全銷毀了自己的AO對象嗎? 這將列印什麼呢?表 ...
  • extend是jQuery中一個比較核心的代碼,如果有查看jQuery的源碼的話,就會發現jQuery在多處調用了extend方法。 作用 1. 對任意對象進行擴展 2. 擴展某個實例對象 3. 對jquery本身的實例方法進行擴展 實現 基礎版本, 對簡單對象進行擴展 jQuery.prototy ...
  • 本架構主要目的是改進軟體開發中松耦合、增加模塊的重用性、提高開發效率。 在常規的模塊間方法直接調用式開發中,新增的功能對原有模塊代碼的穩定性、重用性破壞大,不利於軟體的後期維護,且開發效率低。 另外,在傳統的軟體開發方法中,如果新增的功能的邏輯在其它模塊需要重覆使用,則只能通過copy代碼或方法調用 ...
  • 概述 靜態頁面生成是常用的提升性能手段,將一些高併發、變化頻率低、對延遲容忍度高的頁面生為靜態頁面,在電商場景中首頁、商品詳情頁、幫助中心頁、專題頁都是符合特征的頁面。通過生成靜態頁直接輸出給瀏覽器,能夠有效的減少資料庫及cpu的負載。 一般說來,靜態頁的生成和展示有如下幾個裝置: 頁面生成裝置 頁 ...
  • 一、HAProxy概述: HAProxy提供高可用性、負載均衡以及基於TCP和HTTP應用的代理,支持虛擬主機,它是免費、快速並且可靠的一種解決方案。根據官方數據,其最高極限支持10G的併發。 HAProxy特別適用於那些負載特大的web站點, 這些站點通常又需要會話保持或七層處理。HAProxy運 ...
  • 版權歸作者所有,任何形式轉載請聯繫作者。 作者:tison(來自豆瓣) 來源:https://www.douban.com/note/733279598/ Monad 在實際開發中的應用 不同的人會從不一樣的角度接觸 Monad。大多數網上的教程和介紹都從其嚴格的定義出發,加上幾個玩具示例就當講解完 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...