HDU 5838 (状压DP+容斥)

Problem Mountain

题目大意

  给定一张n*m的地图,由 . 和 X 组成。要求给每个点一个1~n*m的数字(每个点不同),使得编号为X的点小于其周围的点,编号为.的点至少大于一个其周围的点。

    n<=5 , m<=5。

解题分析

  考虑从1~n*m,从小到大依次填数,则如果某个位置编号为X且该位置还未填数,那么其周围的点均不能填数。

  令dp[i][j]表示填到第i个数,状态为j 。 令X的个数为cnt,那么 j ∈[ 0 , 1<<cnt)。

  一种情况为第i个数填在 X 的位置上,那么dp[i][j] 可以由 dp[i-1][j-{x}] 转移过来。 j - {x} 表示去掉某个X后的状态。

  另一种情况为第i个数填在 . 的位置,那么首先预处理一下w[j]表示状态为j时有多少个位置是可以填的,那么dp[i]][j] += dp[i-1][j] * (w[j] - (i-1))

  又因为要保证遍号为.的点至少大于一个其周围的点,dfs容斥一下。

参考程序

  1 #include <map>
  2 #include <set>
  3 #include <stack>
  4 #include <queue>
  5 #include <cmath>
  6 #include <ctime>
  7 #include <string>
  8 #include <vector>
  9 #include <cstdio>
 10 #include <cstdlib>
 11 #include <cstring>
 12 #include <cassert>
 13 #include <iostream>
 14 #include <algorithm>
 15 #pragma comment(linker,"/STACK:102400000,102400000")
 16 using namespace std;
 17 
 18 #define N 100008             
 19 #define M 50008    
 20 #define LL long long
 21 #define lson l,m,rt<<1
 22 #define rson m+1,r,rt<<1|1 
 23 #define clr(x,v) memset(x,v,sizeof(x));
 24 #define rep(x,y,z) for (int x=y;x<=z;x++)
 25 #define repd(x,y,z) for (int x=y;x>=z;x--)
 26 const int mo  = 772002;
 27 const int inf = 0x3f3f3f3f;
 28 const int INF = 2000000000;
 29 /**************************************************************************/
 30 int n,m,ans,dp[30][1024],w[1024],tx[10],ty[10];
 31 char mp[10][10];
 32 int flag[10][10];
 33 const int dx[9]={-1,-1,-1,0,0,0,1,1,1};
 34 const int dy[9]={-1,0,1,-1,0,1,-1,0,1};
 35 void up(int &x,int y){
 36     x = (x + y) % mo;
 37     if (x>=mo) x-=mo;
 38 }
 39 int solve(){
 40     int cnt=0;
 41     rep(i,1,n) rep(j,1,m)
 42         if (mp[i][j]=='X'){
 43             tx[++cnt]=i;
 44             ty[cnt]=j;
 45         }
 46     for (int i=0;i<1<<cnt;i++){
 47         clr(flag,0);
 48         for (int j=1;j<=cnt;j++)
 49             if (i & 1<<j-1)
 50                 for (int k=0;k<9;k++) flag[tx[j]+dx[k]][ty[j]+dy[k]]=1;
 51         w[i]=0;
 52         rep(j,1,n) rep(k,1,m)
 53                 if (!flag[j][k]) w[i]++;
 54     }
 55     clr(dp,0);
 56     dp[0][0]=1;
 57     for (int i=1;i<=n*m;i++)
 58         for (int j=0;j<1<<cnt;j++)
 59         {
 60             for (int k=1;k<=cnt;k++)
 61                 if (j & 1<<k-1) 
 62                     up(dp[i][j],dp[i-1][j - (1<<k-1)]);
 63             up(dp[i][j],dp[i-1][j]*(w[j]-i+1));
 64         }
 65     return dp[n*m][(1<<cnt)-1];
 66 }
 67 void dfs(int x,int y,int p){
 68     if (y==m+1){
 69         x++;
 70         y=1; 
 71     }
 72     if (x==n+1){ 
 73         ans=(ans + solve()*p) % mo;
 74         if (ans<0) ans+=mo;
 75         return;
 76     }
 77     int flag=0;
 78     for (int i=0;i<9;i++) if (i!=4&&mp[x+dx[i]][y+dy[i]]=='X') flag=1;
 79     if (mp[x][y]=='X')
 80         if (flag) return; else dfs(x,y+1,p);
 81     else
 82     {
 83         dfs(x,y+1,p);
 84         if (!flag)
 85         {
 86             mp[x][y]='X';
 87             dfs(x,y+1,-p);
 88             mp[x][y]='.';
 89         }
 90     }
 91 }
 92 int main(){
 93     int cas=0;
 94     while (~scanf("%d %d",&n,&m)){
 95         clr(mp,0); ans=0;
 96         rep(i,1,n) scanf("%s",mp[i]+1);
 97         dfs(1,1,1);
 98         printf("Case #%d: %d
",++cas,ans);
 99     }
100 }
View Code
原文地址:https://www.cnblogs.com/rpSebastian/p/5788009.html