zoukankan      html  css  js  c++  java
  • BZOJ2669 局部极小值

    题目

    有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。

    给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。

    思路

    看到数据范围马上想到状压dp。

    先分析状态数,会发现局部极小值在一张图中最多只有8个,所以状态为已经实现了哪些局部极小值。(dp[i][S])为已经填完数([1,i]),满足局部极小值为(S​)的方案数。

    则考虑从已知的位置向后转移,下一个填数的位置可以是某个未填的局部极小值,也可以是除了未填过的局部最小值旁边的点之外的其它点,我们可以预处理这些点的数目(num[i])

    两个转移方程如下:

    [dp[i+1][S+(1<<j)]leftarrow{dp[i][S]}\ dp[i+1][s]leftarrow{(num[s]−(i−∣s∣))×dp[i][s]} ]

    然后要注意的是状态的定义:我们当前的(dp)值仅仅满足了需要成为局部极小值的点成为了局部极小值,而不能保证是否除了这些点之外还有其它的局部极小值,所以这里做一个容斥就好了。

    代码

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int mod=12345678;
    void Add(int &x,int y){
    	x+=y;
    	if(x<0)x+=mod;
    	if(x>=mod)x-=mod;	
    }
    int n,m;
    char S[15][15];
    struct node{int x,y;}P[10];
    bool check(int x,int y){return x>=1&&x<=n&&y>=1&&y<=m;}
    int dp[30][1<<8|5],tt,cnt[1<<8|5];
    bool vis[15][15],mark[5][8];
    int calc(){
    	for(int i=1;i<1<<tt;i++){
    		memset(mark,0,sizeof(mark));cnt[i]=0;
    		for(int j=0;j<tt;j++)
    			if(i&1<<j){
    				for(int x=P[j].x-1;x<=P[j].x+1;x++)
    					for(int y=P[j].y-1;y<=P[j].y+1;y++){
    						if(!check(x,y)||mark[x][y])continue;
    						mark[x][y]=1;cnt[i]++;
    					}
    			}
    	}
    	dp[0][0]=1;
    	for(int i=1;i<=n*m;i++){
    		for(int j=0;j<1<<tt;j++){
    			dp[i][j]=0;
    			Add(dp[i][j],1LL*dp[i-1][j]*(n*m-cnt[((1<<tt)-1)^j]-i+1)%mod);
    			for(int q=0;q<tt;q++)
    				if(j&1<<q)
    					Add(dp[i][j],dp[i-1][j^(1<<q)]);
    		}
    	}
    	return dp[n*m][(1<<tt)-1];
    }
    int dfs(int x,int y,int f){
    	if(x>n)return calc()*f;
    	if(y>m)return dfs(x+1,1,f);
    	int res=0;
    	if(S[x][y]=='X'){
    		P[tt++]=(node){x,y};
    		Add(res,dfs(x,y+1,f));tt--;
    		return res;
    	}
    	bool flag=1;
    	for(int i=x-1;i<=x+1;i++)
    		for(int j=y-1;j<=y+1;j++)
    			if(S[i][j]=='X'||vis[i][j])flag=0;
    	if(flag){
    		P[tt++]=(node){x,y};vis[x][y]=1;
    		Add(res,dfs(x,y+1,-f));tt--;vis[x][y]=0;
    	}
    	Add(res,dfs(x,y+1,f));
    	return res;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%s",S[i]+1);
    	printf("%d
    ",dfs(1,1,1));
    	return 0;
    }
    
  • 相关阅读:
    [css]继承关系(一)
    Trie树-提高海量数据的模糊查询性能
    [C]struct结构化数据的一些要点
    [C]表达式结合规律和运算符优先级
    [C]副作用和序列点
    [C]链接和生存周期
    [PHP]关于连接MySQL的问题
    IT网址大全
    VUE 生命周期
    Vue 组件间传值
  • 原文地址:https://www.cnblogs.com/zryabc/p/11202115.html
Copyright © 2011-2022 走看看