1. 程式人生 > 實用技巧 >HDU5838 Mountain(狀壓dp+容斥)

HDU5838 Mountain(狀壓dp+容斥)

資料這麼小,不是暴力就是狀壓。

考慮狀壓dp,f[i][j]表示前i大的數填完後,狀態集合為j的情況。

這樣我們可以從小到大填數。對於狀態更新。

首先如果當前i我們填在一個谷底那麼就是狀態直接相加。

比較複雜的情況是我們填在非谷地,因為我們要知道還可以填哪些位置,而這些位置是隨便填的,只要根據乘法原理乘一下就能更新

首先如果一個非谷地旁邊是谷底,那麼不能填,如果已經被填過了,那麼也不能填,我們需要預處理這些狀態可以看我的程式碼理解。

但是這樣求出來並不是答案,因為我們現在滿足的是谷地一定填好了,但是非谷地可能在更新中成為谷地,這個意思是說,我們更新的時候是根據乘法原理更新的

這裡就會存在某個被定義為非谷地的點周圍填的數都比他大,這樣它就成為谷地了,但是這樣是非法的,因此我們考慮去重。

一般這種去重很容易想到容斥原理,將某個可能成為谷地的非谷地人為設計為谷底,之後根據奇偶改變數決定是加還是減,這是容斥的基本套路

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> pll;
const int N=5e5+10;
const int inf=0x3f3f3f3f;
const int mod=772002;
int n,m;
int f[30][1200];
char g[30][30
]; char tmp[30][30]; map<int,pll> m1; int pos[N],mou[N]; ll ans; int dx[]={-1,-1,0,1,1,1,0,-1}; int dy[]={0,1,1,1,0,-1,-1,-1}; int sx[N],sy[N]; bool check(){ int i,j; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ if(g[i][j]!='X') continue; for(int
k=0;k<8;k++){ int x=i+dx[k]; int y=j+dy[k]; if(x&&x<=n&&y&&y<=m){ if(g[x][y]=='X') return true; } } } } return false; } ll solve(){ int i,j; memset(f,0,sizeof f); f[0][0]=1; int cnt=0; int num=n*m; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ if(g[i][j]=='X'){ cnt++; sx[cnt-1]=i; sy[cnt-1]=j; } } } memset(tmp,'.',sizeof tmp); int mx=1<<cnt; for(i=0;i<mx;i++){ mou[i]=0; for(j=0;j<cnt;j++){ if(i>>j&1){ mou[i]++; } else{ tmp[sx[j]][sy[j]]='Y'; } } pos[i]=num-cnt; for(int l=1;l<=n;l++){ for(int r=1;r<=m;r++){ if(g[l][r]=='X') continue; for(int k=0;k<8;k++){ int a=l+dx[k]; int b=r+dy[k]; if(a<=n&&a&&b&&b<=m){ if(tmp[a][b]=='Y'){ pos[i]--; break; } } } } } for(int k=0;k<cnt;k++){ tmp[sx[k]][sy[k]]='.'; } } for(i=0;i<num;i++){ for(j=0;j<mx;j++){ if(!f[i][j]) continue; for(int k=0;k<cnt;k++){ if(!((j>>k)&1)){ f[i+1][j|(1<<k)]=(f[i+1][j|(1<<k)]+f[i][j])%mod; } } f[i+1][j]=(f[i+1][j]+f[i][j]*(pos[j]-i+mou[j])%mod)%mod; } } return f[num][mx-1]; } void dfs(int u,int num){ if(u==n*m+1){ if(num%2==0) ans=(ans+solve())%mod; else ans=(ans-solve()+mod)%mod; return ; } int x=m1[u].first; int y=m1[u].second; int i; for(i=0;i<8;i++){ int a=x+dx[i]; int b=y+dy[i]; if(a&&a<=n&&b&&b<=m){ if(g[a][b]=='X') break; } } if(i==8&&g[x][y]=='.'){ g[x][y]='X'; dfs(u+1,num+1); g[x][y]='.'; } dfs(u+1,num); } int main(){ ios::sync_with_stdio(false); int times=1; while(cin>>n>>m){ m1.clear(); int i,j; int id=0; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ cin>>g[i][j]; m1[++id]={i,j}; } } if(check()){ cout<<"Case #"<<times++<<": "; cout<<0<<endl; } else{ ans=0; dfs(1,0); cout<<"Case #"<<times++<<": "; cout<<ans<<endl; } } return 0; }
View Code