計蒜之道複賽 騰訊消消樂

騰訊消消樂 狀壓dp

題目鏈接
題意是就點進去題目鏈說的那樣.
看一下數據由於n<18 然後考慮用狀壓dp
dp[i][s] 表示消去i次得到的狀態爲s的方案數,1代表刪了0代表還沒刪,當我們枚舉到一個狀態s時,取出所有0位,然後枚舉其中的一段,刪除掉,即可轉移到下一步的該狀態,
例如1010101,即表示當前序列已經被刪剩下3個,然後枚舉這3個數字中的一段如
110,則當前狀態可以轉移到下一步的1010101+0101000狀態.然後是否能轉移則可以預處理出每一個狀態的GCD.

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define  LONG long long
const LONG    INF=0x3f3f3f3f;
const LONG  MOD=1e9+ 7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson  l , mid , rt<< 1
#define rson  mid + 1 ,r , (rt<<1)+1
#define root 1, m , 1
LONG dp[20][(1<<18) + 20] ;
int num[20] ;
int take[(1<<18) + 30] ;
int gcd(int a , int b)
{
    int c = a%b ;
     while(c)   a = b ,b = c , c = a %b ;
        return b;
}
int main()
{
    int n ;
    int K ;
    while(cin >> n>>K)
    {
        for(int i = 1 ; i <= n ; ++i)
        scanf("%d",&num[i]) ;
        clr0(dp) ;
        int bit[20 ] ;
        dp[0][0] = 1;
        for(int i = 1 ; i< (1<< n) ; ++ i)
        {
            int tmp = i ;
            int p = 0 ;
            int t = 0 ;
            clr0(bit) ;
            for(int j = 0 ;j < n ; ++ j)
                if( ( tmp>>j )& 1) bit[++p] = j+1 ;
            int judge = num[bit[1]];
            for(int j =1; j < p ;++j)
                judge = gcd (judge, num[ bit[j+1] ] ) ;
            take[i] = judge ;
        }
        for(int i = 0 ; i < n ; ++i)
            for(int j = 0; j < (1<< n) ; ++j)
            {
                int tmp = j ;
                clr0(bit) ;
                int  p= 0;
                for(int k = 0 ; k < n ; ++ k)
                    if( ( tmp >> k & 1) == 0) bit[++ p] = k ;
                for(int k = 1 ;k <= p ; ++ k)
                {
                    int now = j ;
                    int Del = 0 ;
                    for(int l = k ;l <= p ; ++l )
                    {
                        now += (1<<bit[l]) ;
                        Del += (1<<bit[l]) ;
                        if(take[Del] >= K )
                        {
                            dp[1+i][ now ] += dp[i][j] ;dp[i+1][now] %= MOD ;
                        }

                    }
                }
            }
        LONG ans = 0;
        for(int i = 1; i<= n ; ++i)
            ans = (ans + dp[i][(1<<n)-1] *i ) % MOD ;
//            printf("%d %lld\n",i, dp[i][(1<<n)- 1]) ;
        cout<<ans<<endl;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章