zoukankan      html  css  js  c++  java
  • 【题解】中国象棋

    中国象棋 - 摆上马

    题目大意

    给定 (n imes m) 的棋盘,往棋盘上面摆马,问有多少种摆法可以让马互不攻击。马是中国象棋的马,是可以被阻挡攻击路线的,如题干图中所示。

    题目解法

    看到数据范围,首先想到的就是状压了。我们发现,马涉及的状态实际上是 前两列 ,所以我们需要存下前两列的状态。

    那么我们就有一个自然的状态设计:(f[i][j][k]) 表示第 (i) 行,前两列的状态是 (j,k) 的当前行的方案数。转移顺序很显然直接从上往转移即可。

    下面我们从初始化开始分析这个 (dp.)

    对于第一行,显然状态只有 (f[1][0][0]=2^m,) 直接赋值即可。

    对于第二行,考虑直接暴力处理相邻两行的所有可能性,复杂度 (O(4^m),) 至于具体处理细节,由于只有两行,所以唯一需要判断的就是下面的行能不能攻击到上面的行以及上面的行能不能攻击到下面的行。

    如果可以攻击到,那么需要满足它没有被阻挡并且攻击的位置上有马。简单位运算计算一下即可。

    bool check(int x,int y){
    		for(int i=2;i<m;++i){
    			if(!(y>>i&1))continue;
    			int v=(y>>(i-1))&1;
    			if(v)continue;
    			if(x>>(i-2)&1)return false;
    		}
    		for(int i=2;i<m;++i){
    			if(!(x>>i&1))continue;
    			int v=(x>>(i-1)&1);
    			if(v)continue;
    			if(y>>(i-2)&1)return false;
    		}
    		for(int i=0;i<m-2;++i){
    			if(!(y>>i&1))continue;
    			int v=(y>>(i+1)&1);
    			if(v)continue;
    			if(x>>(i+2)&1)return false;
    		}
    		for(int i=0;i<m-2;++i){
    			if(!(x>>i&1))continue;
    			int v=(x>>(i+1)&1);
    			if(v)continue;
    			if(y>>(i+2)&1)return false;
    		}
    		return true;
    	}
    

    继续思考,到这一步为止我们解决了初始化的问题。考虑下面如何 (dp:)

    一个显然的思路是,枚举第几行,枚举上一行、上上行、上上上行的状态,然后考虑当期行填什么。

    由于当前行能填多少种东西只会被前两行影响,所以这一部分也可以直接预处理,复杂度 (O(8^m).)

    接下来就遇到一个小问题,我们不知道如何继承之前的状态了。由于我们设计的状态不包含当前行的具体状态,所以我们不能直接获得对于前两行状态分别是 (j,k) 时的方案数。

    但是,我们可以直接枚举上上上行的状态,我们发现,如果这个状态可以满足当期枚举的后两行状态,那么对于前一行填什么,它继承的信息都是一样的。也就是说,我们可以直接用这个信息除掉当前情况上一行能填的方案数,就可以得到固定状态的上一行的填马方案数。

    至于三行的马怎么判断合法,先把相邻的马判断掉,那么隔一行的马也很好判断,只有两种情况:要么上面的马走到下面攻击,要么下面的马到上面攻击。直接和两种马一样判断即可。

    bool check(int x,int y,int z){
    		for(int i=0;i<m;++i){
    			if(y>>i&1)continue;
    			if(!(z>>i&1))continue;
    			if(i&&(x>>(i-1)&1))return false;
    			if(i<m-1&&(x>>(i+1)&1))return false;
    		}
    		for(int i=0;i<m;++i){
    			if(y>>i&1)continue;
    			if(!(x>>i&1))continue;
    			if(i&&(z>>(i-1)&1))return false;
    			if(i<m-1&&(z>>(i+1)&1))return false;
    		}
    		return true;
    	}
    

    然后这个除法在模意义下直接预处理逆元即可,复杂度 (O(2^mlog mod))

    (dp) 复杂度是 (O(n imes 8^m).) 写完发现爆空间了。

    由于行对我们的状态影响不大,直接滚动数组即可。

    复杂度 (O(n imes 2^8).)

    #include<bits/stdc++.h>
    using namespace std;
    typedef double db;
    //#define int long long
    const int mod=1e9+7;
    const db eps=1e-14;
    inline int Max(int x,int y){return x>y?x:y;}
    inline int Min(int x,int y){return x<y?x:y;}
    inline db Max(db x,db y){return x-y>eps?x:y;}
    inline db Min(db x,db y){return x-y<eps?x:y;}
    inline int Add(int x,int y,int M=mod){return (x+y)%M;}
    inline int Mul(int x,int y,int M=mod){return 1ll*x*y%M;}
    inline int Dec(int x,int y,int M=mod){return (x-y+M)%M;}
    inline int Abs(int x){return x<0?-x:x;}
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    	while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
    	return s*w;
    }
    inline void write(int x){
    	if(x<0)putchar('-'),x=-x;
    	if(x>9)write(x/10);
    	putchar(x%10+'0');
    }
    inline int qpow(int x,int y){
    	int res=1;
    	while(y){
    		if(y&1)res=Mul(res,x);
    		x=Mul(x,x);y>>=1;
    	}
    	return res;
    }
    typedef pair<int,int> pr;
    #define fi first
    #define se second
    #define mk make_pair
    #define pb emplace_back
    #define poly vector<int>
    namespace Refined_heart{
    	int f[2][1<<6][1<<6],n,m,val[1<<6][1<<6],ival[1<<6][1<<6];
    	bool check(int x,int y){
    		for(int i=2;i<m;++i){
    			if(!(y>>i&1))continue;
    			int v=(y>>(i-1))&1;
    			if(v)continue;
    			if(x>>(i-2)&1)return false;
    		}
    		for(int i=2;i<m;++i){
    			if(!(x>>i&1))continue;
    			int v=(x>>(i-1)&1);
    			if(v)continue;
    			if(y>>(i-2)&1)return false;
    		}
    		for(int i=0;i<m-2;++i){
    			if(!(y>>i&1))continue;
    			int v=(y>>(i+1)&1);
    			if(v)continue;
    			if(x>>(i+2)&1)return false;
    		}
    		for(int i=0;i<m-2;++i){
    			if(!(x>>i&1))continue;
    			int v=(x>>(i+1)&1);
    			if(v)continue;
    			if(y>>(i+2)&1)return false;
    		}
    		return true;
    	}
    	int calc(int state){
    		int res=0;
    		for(int i=0;i<(1<<m);++i)res+=check(i,state);
    		return res;
    	}
    	bool check(int x,int y,int z){
    		for(int i=0;i<m;++i){
    			if(y>>i&1)continue;
    			if(!(z>>i&1))continue;
    			if(i&&(x>>(i-1)&1))return false;
    			if(i<m-1&&(x>>(i+1)&1))return false;
    		}
    		for(int i=0;i<m;++i){
    			if(y>>i&1)continue;
    			if(!(x>>i&1))continue;
    			if(i&&(z>>(i-1)&1))return false;
    			if(i<m-1&&(z>>(i+1)&1))return false;
    		}
    		return true;
    	}
    	void treat(){
    		for(int i=0;i<(1<<m);++i)
    			for(int j=0;j<(1<<m);++j){
    				if(!check(i,j))continue;
    				for(int k=0;k<(1<<m);++k){
    					if(!check(j,k))continue;
    					val[i][j]+=check(i,j,k);
    				}
    			}
    		for(int i=0;i<(1<<m);++i)
    			for(int j=0;j<(1<<m);++j)
    				ival[i][j]=qpow(val[i][j],mod-2);
    	}
    	void MAIN(){
    		n=read();m=read();
    		f[1][0][0]=1<<m;
    		for(int i=0;i<(1<<m);++i)f[0][i][0]=calc(i);
    		treat();
    		for(int i=3;i<=n;++i){
    			int pos=i&1;
    			memset(f[pos],0,sizeof f[pos]);
    			for(int j=0;j<(1<<m);++j){
    				for(int k=0;k<(1<<m);++k){
    					if(!check(k,j))continue;
    					int s=0;
    					for(int l=0;l<(1<<m);++l){
    						if(!check(l,k))continue;
    						if(!check(l,k,j))continue;
    						s=Add(s,Mul(f[pos^1][k][l],ival[l][k]));
    					}
    					f[pos][j][k]=Add(f[pos][j][k],Mul(s,val[k][j])); 
    				}
    			}
    		}
    		int ans=0;
    		for(int i=0;i<(1<<m);++i)
    			for(int j=0;j<(1<<m);++j)
    				ans=Add(ans,f[n&1][i][j]);
    		write(ans);
    	}
    }
    int main(){
    	Refined_heart::MAIN();
    	return 0;
    }
    
    
  • 相关阅读:
    Tomcat原理与实践
    Spring Boot入门与实践
    Docker安装及使用
    JDK源码解析——集合(一)数组 ArrayList
    浅谈mysql底层索引
    微信小程序全局配置知识点
    uniapp全屏高度
    npm node-sass报错
    微信小程序接口配置问题
    微信小程序的设计流程
  • 原文地址:https://www.cnblogs.com/h-lka/p/15480754.html
Copyright © 2011-2022 走看看