hdu - 4332 - Constructing Chimney - 狀態壓縮dp

題意:http://acm.hdu.edu.cn/showproblem.php?pid=4332

有一塊3*3的空地,要求在上面建一個建築物,但是要求中間的那塊必須空着,現在只有1*1*2的磚,問你要建成高度爲N的建築物共有多少種不同的方法。N<=1e9

思路:
看到這麼大的N就應該想到用矩陣二分冪來求,那麼很顯然就是dp了,因爲磚只有1*1*2的一種,在對一層進行排放的時候我們只需關心它的上一層的擺放情況就好了, 因此我們需要用一個8bit的二進制來記錄上一層的狀態,接下去我們就要構造出一個轉移矩陣。先考慮一下複雜度:O(256^3*log(n) ),這樣的複雜度有有可能會超時(但是事實是題目給的20s時限這樣的複雜度也可以過)。這樣我們就希望能優化上面的複雜度,logn的矩陣乘法是不能優化了,那麼我們就想優化前面的256^3,看是否其中的有些狀態是可以去掉的。考慮題目中的8個位置正好是矩陣的外圈,用8位的2進製表示時有對稱性,如果將4個對稱的部分合在一起,那麼狀態數就會變成只有256/4種(其實有70種),這樣我們就可以將複雜度降低了。最後的工作就是構造出矩陣,然後用矩陣二分冪求解。 在構造矩陣的時候要注意去掉重複的。

#include <stdio.h>
#include <string.h>
#include <map>
#include <algorithm>

typedef __int64 LL ;
const LL Mod = 1000000007LL ;

const int m = (1 << 8) ;
const int mm = 70 ;
bool ok[m]  ;
int a[8] , N ;
int min[m] ;

struct Matrix{
    LL mat[mm][mm] ;
    void init(){
        memset( mat,  0 ,sizeof(mat) ) ;
    }
}M[31] , res ,mid ;
LL ans[mm] ;

bool is_ok(int s){
    if( s==0 )  return true ;
    if( s==m-1 )  return true ;
    int val[8] ;
    int i ;
    for(i=0;i<8;i++)
        if( s&a[i] )    val[i] = 1 ;
        else    val[i] = 0;
    i = 0 ;
    while( val[i] ) ++ i ;
    for(int ii=0;ii<8;ii++  ){
        if( val[ (ii+i)%8 ] == 0 )   continue ;
        if( ii+1<8 && val[ (ii+i+1)%8 ] ){
            ii ++ ;
        }
        else    return false ;
    }
    return true ;
}

int getmin( int n ){
    int val[8] ;
    for(int i=0;i<8;i++) {
        if( n&a[i] )    val[i] = 1 ;
        else    val[i] = 0 ;
    }
    int v = 0 ;
    int res = n ;
    for(int ii=0;ii<8;ii++)
        if( val[ (2+ii)%8 ] )   v |= a[ ii ]  ;
    if( res > v )   res = v ;
    v = 0 ;
    for(int ii=0;ii<8;ii++)
        if( val[ (4+ii)%8 ] )   v |= a[ ii ]  ;
    if( res > v )   res =v ;
    v = 0 ;
    for(int ii=0;ii<8;ii++)
        if( val[ (6+ii)%8 ] )   v |= a[ ii ]  ;
    if( res > v )   res =v ;
    return res ;
}

std::map<int , int> mp ;
bool vis[m + 10][m + 10]  ;

void deal(int sou , int to){
    int val[8]  ;
    for(int i=0;i<8;i++){
        if( to&a[i] )  val[i] = 1 ;
        else    val[i] = 0 ;
    }
    LL v1 , v2 ;
    v1 = sou ;
    for(int j=0; j<=6;j+=2 ){
        v2 = 0  ;
        for(int jj=0;jj<8;jj++){
            if( val[ (j+jj)%8 ] )  v2 |= a[jj]  ;
        }
        if( ( v1|v2 )==m-1 && ok[ v1&v2 ] && !vis[v1][v2] ){
            M[0].mat[ mp[ min[v1] ] ][ mp[ min[v2] ] ] ++ ;
            vis[v1][v2] = 1 ;
        }
    }
}

void build_matrix(){
    M[0].init() ;
    memset( vis , 0 , sizeof(vis) );
    std::map<int, int>::iterator it1 ,it2  ;

    for( it1 = mp.begin() ; it1!=mp.end() ; it1++ ){
        for( it2=mp.begin() ; it2!=mp.end() ; it2++ ){
            deal( it1->first , it2->first ) ;
        }
    }
    M[0].mat[mm-1][mm-1] = 2 ;
    for(int ii=1;ii<=30;ii++){
        for(int i=0;i<mm;i++){
                for(int j=0;j<mm;j++){
                    mid.mat[i][j] = 0 ;
                    for(int k=0;k<mm;k++){
                        mid.mat[i][j] = ( mid.mat[i][j] + M[ii-1].mat[i][k] * M[ii-1].mat[k][j] % Mod ) % Mod ;
                    }
                }
            }
        M[ii] = mid ;
    }
}

void init(){
    a[0] = 1 ;
    for(int i=1;i<8;i++)    a[i] = a[i-1]<<1 ;
    for(int i=0;i<m;i++)    min[i] = getmin(i)  ;
    for(int i=0;i<m;i++){
        ok[i] = is_ok( i ) ;
        mp[ min[i] ] = 1 ;
    }
    std::map<int,int>::iterator it = mp.begin() ;
    int ss = 0 ;
    for( it ; it != mp.end(); it ++ ){
        it->second = ss ++ ;
    }
    build_matrix() ;
}

void calc( int n ){
    memset( ans , 0 , sizeof(ans) )  ;
    int jj = 0 ;
    ans[ mm-1 ] = 1 ;
    LL CC[mm] ;
    while( n ){
        if( n&1 ){
            for(int i=0;i<mm;i++){
                CC[i] = 0;
                for(int j=0;j<mm;j++){
                    CC[i] = ( CC[i] + ans[j] * M[jj].mat[i][j]% Mod ) % Mod ;
                }
            }
            for(int i=0;i<mm;i++)  ans[i] = CC[i] ;
        }
        n >>= 1 ; jj ++ ;
    }
}

int main(){
    init() ;
    int T ;scanf("%d",&T) ;
    int cas = 0 ;
    while( T-- ){
        scanf("%d",&N) ;
        calc(N + 1) ;
        printf("Case %d: %I64d\n",++cas,ans[0] ) ;
    }
    return 0 ;
}


發佈了177 篇原創文章 · 獲贊 2 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章