【HDU】5771 Turn Game 【dp套dp】

题目链接:Turn Game

dp套dp。逐行dp,枚举每一行的状态,向下转移。状态即每一列上是否有列覆盖住这一行。

#include <bits/stdc++.h>
using namespace std ;

typedef long long LL ;
typedef vector < int > vi ;
typedef map < LL , int > mpvi ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int mod = 1e9 + 7 ;

int val[16] = { 0 , 1 , 1 , 1 , 1 , 2 , 1 , 1 , 1 , 2 , 2 , 2 , 1 , 2 , 1 , 1 } ;
int num[16] = { 0 , 1 , 1 , 2 , 1 , 2 , 2 , 3 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 } ;
int res[5][11][41] ;
int tmp[17] , nxt[17] ;
LL p[20] ;
mpvi dp[2] ;
mpvi :: iterator it ;

void up ( int& x , int y ) {
    x += y ;
    if ( x >= mod ) x -= mod ;
}

void init ( int a[] , int x , int y , int n ) {
    a[n] = x ;
    for ( int i = 0 ; i < n ; ++ i ) {
        a[i] = y + x ;
    }
}

void decode ( LL a , int n ) {
    int x = a / p[n] ;
    tmp[n] = x ;
    for ( int i = 0 ; i < n ; ++ i ) {
        tmp[i] = a % 10 + x ;
        a /= 10 ;
    }
}

LL encode ( int a[] , int n ) {
    LL res = a[n] ;
    for ( int i = n - 1 ; ~i ; -- i ) {
        res = res * 10 + ( a[i] - a[n] ) ;
    }
    return res ;
}


void preprocess () {
    p[0] = 1 ;
    for ( int i = 1 ; i < 20 ; ++ i ) {
        p[i] = p[i - 1] * 10 ;
    }
    for ( int m = 1 ; m <= 4 ; ++ m ) {
        int cur = 0 ;
        init ( tmp , 0 , 6 , 1 << m ) ;
        tmp[0] = 0 ;
        dp[0].clear () ;
        dp[0][encode ( tmp , 1 << m )] = 1 ;
        for ( int n = 1 ; n <= 10 ; ++ n ) {
            cur ^= 1 ;
            dp[cur].clear () ;
            mpvi& pre = dp[cur ^ 1] ;
            for ( it = pre.begin () ; it != pre.end () ; ++ it ) {
                decode ( it->first , 1 << m ) ;
                for ( int i = 0 ; i < 1 << m ; ++ i ) {
                    init ( nxt , 41 , 0 , 1 << m ) ;
                    int minv = 41 ;
                    for ( int j = 0 ; j < 1 << m ; ++ j ) if ( tmp[j] <= n + m - 1 ) {
                        for ( int k = 0 ; k < 1 << m ; ++ k ) {
                            int x = tmp[j] + num[k] - num[j & k] + val[i ^ k] ;
                            nxt[k] = min ( nxt[k] , x ) ;
                            minv = min ( x , minv ) ;
                        }
                    }
                    nxt[1 << m] = minv ;
                    up ( res[m][n][minv] , it->second ) ;
                    up ( dp[cur][encode ( nxt , 1 << m )] , it->second ) ;
                }
            }
            for ( int i = 1 ; i <= 40 ; ++ i ) {
                up ( res[m][n][i] , res[m][n][i - 1] ) ;
            }
        }
    }

}

int main () {

    preprocess () ;
    //printf ( "%.5f\n" , ( double ) clock () / CLOCKS_PER_SEC ) ;
    /*
    printf ( "res[5][11][41] = { " ) ;
    for ( int i = 0 ; i <= 4 ; ++ i ) {
        for ( int j = 0 ; j <= 10 ; ++ j ) {
            for ( int k = 0 ; k <= 40 ; ++ k ) {
                printf ( "%d , " , res[i][j][k] ) ;
            }
        }
    }
    printf ( "} ;\n" ) ;
    */
    int T , n , m , k ;
    scanf ( "%d" , &T ) ;
    for ( int i = 1 ; i <= T ; ++ i ) {
        scanf ( "%d%d%d" , &n , &m , &k ) ;
        printf ( "Case #%d: %d\n" , i , res[n][m][k] ) ;
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值