zoukankan      html  css  js  c++  java
  • P3160 [CQOI2012]局部极小值 题解(状压DP+容斥)

    题目链接

    P3160 [CQOI2012]局部极小值

    双倍经验,双倍快乐

    解题思路

    存下来每个坑(极小值点)的位置,以这个序号进行状态压缩。

    显然,(4*7)的数据范围让极小值点在8个以内(以下示意)

    X . X . X . X .
    . . . . . . . .
    X . X . X . X .
    . . . . . . . .

    所以考虑用(S)表示各个极小值点是否已填的状态,枚举(1-n*m)进行状压(DP)

    当前填的数有两种选择:

    (1))填入坑中,这样枚举(S)状态表示的每一个已填的坑,(f[i][S]+=)(sum_{k|((1<<k)&S==0)}^{} {f[i-1][S-(1<<k)]})(k)表示第(k)个坑是没填过的)(这步的意思是:(S-(1<<k))这个状态没有填(k)位置的坑,我现在(i)要填这个坑的种类数就是(f[i-1][S-(1<<k)])

    (2))不填入坑中,这样枚举每一个点,如果可以填(指如果有坑还没填,那么他旁边以及他本身共(9)个格都不能填东西,否则这个坑继续往后填填不成极小值),那么就是一种新的填法,数出来(tot)种填法,那么(dp[i][S]+=dp[i-1][S]*(tot-i+1))。然后发现枚举不是很好,可以进行预处理,处理出每一个状态对应的(tot)(代码中用(hi[S])表示)。

    然后答案就是(f[m*n][(1<<num)-1])。((num)代表坑的个数)

    然后因为是胡乱填数,于是可能会出现不应该是坑的地方变成坑的情况,于是就在每个可能出现坑的地方分别新加一个坑进行(DP),完了再减去。而这个(DP)又带来新的可能填的坑......而这就是个容斥问题了。

    AC代码

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    char a[10];
    int m,n,min[6][10];//描述整个矩阵 
    int num,x[30],y[30];//描述坑的个数、位置 
    int w=12345678;//膜 
    int dx[10]={-1,-1,-1,0,0,1,1,1,0};//移动位置 
    int dy[10]={-1,0,1,-1,1,-1,0,1,0};
    int vis[6][10],f[29][(1<<8)+10],hi[1<<9];//dp用到的东西 
    int dp(){
    	int i,j,k;
    	memset(f,0,sizeof(f));
    	f[0][0]=1;
    	for(i=0;i<(1<<num);i++){//预处理出每个状态i对应的可填点数量 
    		hi[i]=n*m;
    		memset(vis,0,sizeof(vis));
    		for(j=0;j<num;j++)if(!(i&(1<<j)))for(k=0;k<9;k++)vis[x[j]+dx[k]][y[j]+dy[k]]=1;
    		for(j=1;j<=n;j++)for(k=1;k<=m;k++)if(vis[j][k])hi[i]--;
    	}
    	for(i=1;i<=n*m;i++){//枚举填哪个数 
    		for(j=0;j<(1<<num);j++){//枚举状态 
    			if(hi[j]-i+1>0)f[i][j]+=f[i-1][j]*(hi[j]-i+1),f[i][j]%=w;
    			for(k=0;k<num;k++)if((1<<k)&j)f[i][j]+=f[i-1][j^(1<<k)],f[i][j]%=w;
    		}
    	}
    	return f[n*m][(1<<num)-1];
    }
    int dfs(int X,int Y){
    	if(Y==m+1)X++,Y=1;
    	if(X==n+1)return dp();
    	int i,ans=dfs(X,Y+1);
    	for(i=0;i<9;i++)if(min[X+dx[i]][Y+dy[i]])return ans;
    	//如果没return说明这个地方是个可能变成坑的地方,那就把它变成坑dfs一下 
    	x[num]=X;y[num++]=Y;min[X][Y]=1;
    	ans-=dfs(X,Y+1),ans=(ans+w)%w;
    	min[X][Y]=0;num--;//别忘了变回去 
    	return ans;
    }
    int main(){
    	int i,j;
    	scanf("%d%d",&n,&m);
    	for(i=1;i<=n;i++){
    		scanf("%s",a+1);
    		for(j=1;j<=m;j++)if(a[j]=='X'){
    			min[i][j]=1;
    			x[num]=i;y[num++]=j;
    		}
    	}
    	for(i=0;i<num;i++)for(j=0;j<i;j++)if(abs(x[i]-x[j])<2&&abs(y[i]-y[j])<2)return printf("0"),0;
    	if(!num)return printf("0"),0;
    	printf("%d",dfs(1,1));
    	return 0;
    }
    
  • 相关阅读:
    python gevent(协程模块)
    python基础之socket与socketserver
    python 使用 with open() as 读写文件
    Python logger模块
    python二维码操作:QRCode和MyQR入门
    常见的端口号及其用途
    python中hasattr()、getattr()、setattr()函数的使用
    mysql数据库自带数据库介绍
    关于BeautifulSoup4 解析器的说明
    学习opencv(1)
  • 原文地址:https://www.cnblogs.com/Potassium/p/10467647.html
Copyright © 2011-2022 走看看