zoukankan      html  css  js  c++  java
  • ●BZOJ 2669 [cqoi2012]局部极小值

    题链:

    http://www.lydsy.com/JudgeOnline/problem.php?id=2669

    题解:

    容斥,DP,DFS


    先看看 dp 部分:
    首先呢,X的个数不会超过 8个
    个数很少,所以考虑状压,把需要填 X的那几个位置状压为二进制10表示对应的那个X位置是否已经填数。
    同时填的数互不重复,考虑从小填到大。
    cnt[S] 表示除了不在集合 S 里的 X 位置及其周围的位置,剩下的位置个数
    定义 dp[i][S]表示从小到大填数填完了i这个数,且已经填了的 S 这个集合里的 X 位置的方案数
    转移:依次去填数 1~N*M,每次有两种选择:
    1).把这个数填在 某个 X 位置(枚举一个 k表示第 k个 X 位置填当前数)
    dp[i][S]+=dp[i-1][S^(1<<(k-1))]

    2).把这个数填在非 X 位置,那么填的位置有 cnt[S]-(i-1) 种。
    dp[i][s]+=dp[i-1][s]*(cnt[s]-(i-1)) (好好理解一下这个转移)

    这样 dp 可以保证那些给出的 X 位置一定是局部最小值,
    因为第二种转移的填数位置都不能填在还没有填数的 X 位置的周围。
    所以就完了么?
    当然还没有,尽管我们保证了给出的 X 位置一定是局部最小值,
    但是没有保证非 X位置一定不是非局部最小值。即,求出来的 dp[N*M][all_S(全集)]的意思是至少all_S集合里的 X位置为局部最小值的方案数
    所以容斥如下:
    ANS = 至少多填了0个局部最小值的方案数(dp[N*M][all_S])
              -至少多填了1个局部最小值的方案数
             +至少多填了2个局部最小值的方案数
              -....+ ....
    这些用于容斥的方案数的求法:
    DFS 搜索出哪些非 X 位置还可以改为 X ,
    然后对于每一种新的填法,去跑一遍上述的dp即可求得对应的方案数。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define _ % mod
    #define filein(x) freopen(#x".in","r",stdin);
    #define fileout(x) freopen(#x".out","w",stdout);
    using namespace std;
    const int mv[9][2]={{0,0},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
    const int mod=12345678;
    char mp[10][10];
    int N,M,ANS;
    int solve(){
    	static bool vis[10][10];
    	static int dp[30][1<<8],cnt[1<<8],x[10],y[10],tot,tmp;
    	tot=0; memset(dp,0,sizeof(dp));
    	for(int i=1;i<=N;i++)
    		for(int j=1;j<=M;j++) if(mp[i][j]=='X') 
    			tot++,x[tot]=i,y[tot]=j;
    	for(int s=0;s<1<<tot;s++){
    		tmp=0; memset(vis,0,sizeof(vis));
    		for(int i=1;i<=tot;i++) if(!(s&(1<<(i-1))))
    			for(int k=0;k<9;k++)
    				vis[x[i]+mv[k][0]][y[i]+mv[k][1]]=1;
    		for(int i=1;i<=N;i++)
    			for(int j=1;j<=M;j++) 
    				if(!vis[i][j]) tmp++;
    		cnt[s]=tmp;
    	}
    	dp[0][0]=1;
    	for(int i=1;i<=N*M;i++)
    		for(int s=0;s<1<<tot;s++){
    			dp[i][s]=(1ll*dp[i][s]+1ll*dp[i-1][s]*max(cnt[s]-(i-1),0)_)_;
    			for(int k=1;k<=tot;k++) if(s&(1<<(k-1)))
    				dp[i][s]=(1ll*dp[i][s]+dp[i-1][s^(1<<(k-1))])_;
    		}
    	return dp[N*M][(1<<tot)-1];
    }
    void dfs(int x,int y,int t){
    	if(y==M+1){dfs(x+1,1,t);return;}
    	if(x==N+1){
    		int tmp=solve();
    		if(t&1) tmp=(-1ll*tmp+mod)_;
    		ANS=((1ll*ANS+tmp)_+mod)_;
    		return;
    	}
    	dfs(x,y+1,t);
    	bool fg=1;
    	for(int k=0;k<9;k++) 
    		if(mp[x+mv[k][0]][y+mv[k][1]]=='X') fg=0;
    	if(fg){
    		mp[x][y]='X';
    		dfs(x,y+1,t+1);
    		mp[x][y]='.';
    	}
    }
    int main()
    {
    	scanf("%d%d",&N,&M);
    	for(int i=1;i<=N;i++) 
    		scanf("%s",mp[i]+1);
    	dfs(1,1,0);
    	printf("%d",ANS);
    	return 0;
    }

  • 相关阅读:
    网络七层
    微信小程序开发工具 常用快捷键
    BZOJ 1026 windy数 (数位DP)
    BZOJ 1026 windy数 (数位DP)
    CodeForces 55D Beautiful numbers (SPOJ JZPEXT 数位DP)
    CodeForces 55D Beautiful numbers (SPOJ JZPEXT 数位DP)
    HDU 3709 Balanced Number (数位DP)
    HDU 3709 Balanced Number (数位DP)
    UVA 11361 Investigating Div-Sum Property (数位DP)
    UVA 11361 Investigating Div-Sum Property (数位DP)
  • 原文地址:https://www.cnblogs.com/zj75211/p/8035053.html
Copyright © 2011-2022 走看看