1. 程式人生 > >暴力分塊矩陣乘法

暴力分塊矩陣乘法

題引

題解

樸素的演算法 O(4096 * 64 * 4096) = O(1e9) 不用想是超時的。

因為每次矩陣乘法中存在很多重複的計算。

考慮將矩陣進行分塊優化。預處理出每塊的值。

怎麼分塊。考慮對A矩陣的列分塊,和B矩陣的行分塊。因為p是公共的邊,且p <= 64

需要注意到的是 B矩陣中的取值僅有01那麼如果對B矩陣進行分塊的話。考慮每塊8個01串。那麼每一塊的取值為[0,255]

於是我們預處理出A[i][j][0,255]的每種取值。i表示A矩陣的i行。j表示每行的第幾塊數。[0,255]表示當前塊與所有01序列的取值。

A切完一共4096*8塊,每塊都預處理出256種情況,預處理的空間是4096*8*256,時間是4096*8*8*256。

那麼在進行乘的時候就按照分好的塊再去做乘法。時間為 4096 * 4096 * 8

程式碼

#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++) 
using namespace std;
const int maxn = 4100;
int A[maxn][70],B[maxn][70];
int RA[maxn][10][260],RB[10][maxn];
char str[70];
int n,p,m;
inline void debug() {
    rep(i,0,n-1) {
        rep(j,0,p-1) printf("%d ",A[i][j]);
        printf("\n");
    }
    rep(i,0,p-1) {
        rep(j,0,m-1) printf("%d ",B[i][j]);
        printf("\n");
    }
}
inline void solve() {
    memset(A,0,sizeof(A));
    memset(B,0,sizeof(B));
    memset(RA,0,sizeof(RA));
    memset(RB,0,sizeof(RB));
    rep(i,0,n-1) rep(j,0,p-1){
        scanf("%s",str);int len = strlen(str),temp = 0;
        rep(k,0,len-1) {
            if(str[k] >= '0' && str[k] <= '9') temp = temp * 16 + str[k] - '0';
            else temp = temp * 16 + str[k] - 'A' + 10;
        }
        A[i][j] = temp;
    }
    rep(i,0,m-1) {
        scanf("%s",str);
        rep(j,0,p-1) B[i][j] = str[j]-'0';
    }
    p = (p-1)/8+1;  //將p進行分塊,每8個一塊
    rep(i,0,n-1) rep(j,0,p-1) {
        int base = j * 8;
        rep(k,0,255) rep(l,0,7) if(k & (1<<l)) RA[i][j][k] += A[i][base+l];
    }
    rep(i,0,p-1) rep(j,0,m-1) {
        int base = i * 8;
        rep(l,0,7) RB[i][j] += (B[j][base+l] << l);
    }
    int res = 0;
    rep(i,0,n-1) rep(j,0,p-1) rep(k,0,m-1) {
        res ^= RA[i][j][RB[j][k]];
    }
    printf("%d\n",res);
}
int main()
{
    while(~scanf("%d%d%d",&n,&p,&m)) {
        solve();
        //debug();
    }
    return 0;
}