插頭 dp
阿新 • • 發佈:2020-09-04
插頭dp
洛谷 黑題板子?
P5056
給出n×m的方格,有些格子不能鋪線,其它格子必須鋪,形成一個閉合迴路。問有多少種鋪法?
1、輪廓線
簡單地說,輪廓線就是已決策格子和未決策格子的分界線;
2,插頭dp以每一個格子進行一次轉移;
3,一般設 dp[i][j][state]為(i,j)位置,狀態為state的方案數(或者代價,等等讓你求的東西……)
所以我們狀壓什麼呢?輪廓線。
DP求解棋盤問題是逐格轉移的。所以已經轉移過的格子和沒轉移過的格子被一個折線分成了兩半兒。這個折線就是輪廓線。
注意輪廓線狀態來確定用幾進位制數表示,例如這道題有三種狀態可以用三進製表示,但是太麻煩 蒟蒻不會 ;
可以用四進位制,因為我們一般用的都是二進位制的運算,我們可以用兩個二進位制數表示一個四進位制數;
可以用雜湊表儲存狀態,
4,一般的,對於dp陣列,我們可以滾動
一些細節程式碼裡看,由於我還沒寫,先用學長的;
// luogu-judger-enable-o2 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define int ll #define maxn 100010 #define mod 1926223 using namespace std; inline int read() { int x = 0 , f = 1 ; char ch = getchar() ; while(!isdigit(ch)) { if(ch == '-') f = -1 ; ch = getchar() ; } while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0' , ch = getchar() ; return x * f ; } int n , m , hash[mod + 1] , dp[2][mod + 1] , vis[2][mod + 1] , cnt[2] ; int now , mp[22][22] , endx , endy , ans ; char opt[22] ; inline void insert(int x , int k) { int tmp = x % mod ; while(hash[tmp]) { if(vis[now][hash[tmp]] == x) { dp[now][hash[tmp]] += k ; return ; } tmp = (tmp + 1) % mod ; // cout << "!" << endl ; } hash[tmp] = ++cnt[now] ; vis[now][cnt[now]] = x ; dp[now][cnt[now]] = k ; } inline void work() { dp[0][1] = 1 ; cnt[0] = 1 ; vis[0][1] = 0 ; for(int i = 1 ; i <= n ; ++i) { for(int j = 1 ; j <= m ; ++j) { cnt[now ^= 1] = 0 ; memset(hash , 0 , sizeof hash) ; for(int k = 1 ; k <= cnt[now ^ 1] ; ++k) { int S = vis[now ^ 1][k] , L = (S >> ((j - 1) * 2)) & 3 , R = (S >> (j << 1)) & 3 ;//注意這個就是取出捆綁的兩個二進位制數; int val = dp[now ^ 1][k] ; if(!mp[i][j]) { // if(!L && !R) insert(S , val) ; continue ; } if(!L && !R) { if(mp[i+1][j] && mp[i][j+1]) insert(S ^ (1 << ((j - 1) << 1)) ^ (2 << (j << 1)) , val) ; } if(!L && R) { if(mp[i][j+1]) insert(S , val) ; if(mp[i+1][j]) insert(S ^ (R << (j << 1)) ^ (R << ((j - 1) << 1)) , val) ; } if(L && !R) { if(mp[i+1][j]) insert(S , val) ; if(mp[i][j+1]) insert(S ^ (L << ((j - 1) << 1)) ^ (L << (j << 1)) , val) ; } if(L == 1 && R == 1) { int du = 0 ; for(int p = j + 1 ; ; ++p) { int state = (S >> ((p - 1) << 1)) & 3 ; if(state == 1) du ++ ; if(state == 2) du -- ; if(du == 0) { int dou = S ^ (1 << ((j - 1) << 1)) ^ (1 << (j << 1)) ; insert(dou ^ (2 << ((p - 1) << 1)) ^ (1 << ((p - 1) << 1)) , val) ; break ; } } } if(L == 2 && R == 2) {#nvluf ec j int du = 0 ; for(int p = j ; ; --p) { int state = (S >> ((p - 1) << 1)) & 3 ; if(state == 1) du ++ ; if(state == 2) du -- ; if(du == 0) { int dou = S ^ (2 << ((j - 1) << 1)) ^ (2 << (j << 1)) ; insert(dou ^ (1 << ((p - 1) << 1)) ^ (2 << ((p - 1) << 1)) , val) ; break ; } } } if(L == 2 && R == 1) insert(S ^ (2 << ((j - 1) << 1)) ^ (1 << (j << 1)) , val); if(L == 1 && R == 2 && i == endx && j == endy) ans += val ; } } for(int j = 1 ; j <= cnt[now] ; ++j) vis[now][j] <<= 2 ; } printf("%lld\n" , ans) ; } signed main() { n = read() , m = read() ; for(int i = 1 ; i <= n ; ++i) { scanf("%s" , opt + 1) ; for(int j = 1 ; j <= m ; ++j) if(opt[j] == '.') mp[i][j] = 1 , endx = i , endy = j ; } work() ; }