1. 程式人生 > >BZOJ 2669 CQOI2012 局部極小值 狀壓dp+容斥原理

BZOJ 2669 CQOI2012 局部極小值 狀壓dp+容斥原理

子集 lib 狀壓dp void color 一次 不出 等你 vector

題目鏈接:http://www.lydsy.com/JudgeOnline/problem.php?id=2669

題意概述:實際上原題意很簡潔了我就不寫了吧。。。。

二話不說先觀察一下性質,首先棋盤很窄,可以亂搞的樣子,然後註意到如果一個點是局部極小值那麽周圍3*3矩陣內不能有另一個局部最小值。於是畫個圖發現題目的數據範圍最多有8個局部最小值。性質大概就是這些了。

暴力實際上是搜索,本質是多階段決策問題。由於棋盤很小,容易讓人聯想到搞個插頭dp之類東西來弄一下,依次填每個格子來作為一個決策階段。然後就發現。。。這個東西太復雜了。。。等你想出來不知道什麽時候去了(而且很有可能想不出來)。。。於是需要換個思路,把決策階段改一下,每一行作為階段的話更加復雜。。。棄療。那麽決策階段很有可能就不是按照格子的順序來的了。註意到局部最小值一定是周圍的格子裏面最小的那個,並且最多只有8個局部最小值,那麽嘗試從小到大填充每一個數字作為階段,把局部最小值是否填充的狀態壓進去。

於是令f(s,i)表示用1~i的數字填充棋盤,集合s中的局部最小值已經被填充的方案數。

分析這種決策下的性質,發現一個局部最小值一定是以其為中心3*3內最先填的那個,也就是說如果一個局部最小值沒有填充,那麽周圍3*3的點都不能填充。排除所有不可以填充的點剩下的就是可以填充的點,令cnt[s]表示局部極小值填充狀態為s時的棋盤上最多有幾個數字。

得到f(s,i)=f(s,i-1)*C(cnt[s]-i+1,1)+sum{ f(s-{j},i-1) | j是s的子集 },時間復雜度O(N*M*X*2^X),X表示棋盤上有多少個局部極小值。

但是可以註意到在狀態設計的時候可以保證題目給出的X都成為局部最小值,但是可能讓不是局部最小值的位置變成局部最小值。

於是這題最神的地方來了:容斥!由於棋盤很小,所以說打個回溯跑一下局部極小值的分布位置發現最多也就16000多的方案。我們用回溯跑出每一種題目要求位置為局部極小值的棋盤狀態,對於每個狀態來一次dp,然後根據有多少個額外的點是局部極小值進行奇偶容斥就得出答案。

時間復雜度O(O(容斥)*N*M*X*2^X)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7
#include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 const int maxn=6; 14 const int maxm=9; 15 const int maxs=(1<<8)+2; 16 const int mo=12345678; 17 18 int N,M,X,ans; 19 char mp[maxn][maxm]; 20 struct XY{ int x,y; }p[10]; 21 int bin[10],f[maxs][4*7+5],cnt[maxs],vis[maxn][maxm],sz[maxs]; 22 23 void data_in() 24 { 25 scanf("%d%d",&N,&M); 26 for(int i=1;i<=N;i++){ 27 scanf("%s",mp[i]+1); 28 for(int j=1;j<=M;j++) 29 if(mp[i][j]==X) p[++X]=(XY){i,j}; 30 } 31 for(int i=1;i<=9;i++) bin[i]=1<<i-1; 32 for(int s=1;s<bin[9];s++) sz[s]=sz[s>>1]+(s&1); 33 } 34 void dp(int tot) 35 { 36 for(int s=0;s<bin[tot+1];s++){ 37 cnt[s]=0; 38 memset(vis,0,sizeof(vis)); 39 for(int j=1;j<=tot;j++) if(!(bin[j]&s)){ 40 int x=p[j].x,y=p[j].y; 41 vis[x-1][y-1]=vis[x-1][y]=vis[x-1][y+1]= 42 vis[x][y-1]=vis[x][y]=vis[x][y+1]= 43 vis[x+1][y-1]=vis[x+1][y]=vis[x+1][y+1]=1; 44 } 45 for(int i=1;i<=N;i++) 46 for(int j=1;j<=M;j++) cnt[s]+=1-vis[i][j]; 47 } 48 memset(f,0,sizeof(f)); 49 f[0][0]=1; 50 for(int i=1;i<=cnt[0];i++) 51 f[0][i]=f[0][i-1]*(cnt[0]-i+1)%mo; 52 for(int s=1;s<bin[tot+1];s++) 53 for(int i=sz[s];i<=cnt[s];i++){ 54 f[s][i]=f[s][i-1]*(cnt[s]-i+1)%mo; 55 for(int j=1;j<=tot;j++) if(bin[j]&s) 56 f[s][i]=(f[s][i]+f[s^bin[j]][i-1])%mo; 57 } 58 } 59 bool check(int x,int y) { return mp[x-1][y-1]!=X&&mp[x-1][y]!=X&&mp[x-1][y+1]!=X&&mp[x][y-1]!=X; } 60 void dfs(int x,int y,int n) 61 { 62 if(x>N){ 63 dp(X+n); 64 if(n%2==0) ans=(ans+f[bin[X+n+1]-1][N*M])%mo; 65 else ans=(ans-f[bin[X+n+1]-1][N*M]+mo)%mo; 66 return; 67 } 68 int xx=x,yy=y+1; 69 if(yy>M) xx++,yy=1; 70 if(mp[x][y]==X){ 71 if(check(x,y)) dfs(xx,yy,n); 72 } 73 else{ 74 if(check(x,y)){ 75 mp[x][y]=X,p[X+n+1]=(XY){x,y}; 76 dfs(xx,yy,n+1); 77 mp[x][y]=.; 78 } 79 dfs(xx,yy,n); 80 } 81 } 82 void work() 83 { 84 dfs(1,1,0); 85 printf("%d\n",ans); 86 } 87 int main() 88 { 89 data_in(); 90 work(); 91 return 0; 92 }

BZOJ 2669 CQOI2012 局部極小值 狀壓dp+容斥原理