zoukankan      html  css  js  c++  java
  • P5005 中国象棋

    题目链接

    题意分析

    看数据范围就应该知道这是一道状压DP题了

    一开始想的是设置dp[i][a] 表示第i行状态为a的方案数

    那么状态转移的时候 (dp[i][a]=sum_{b=0}^{2^m-1}dp[i-1][b]) 并且满足a与b并不冲突

    但是要注意的是影响第i行的不只有第i-1行 还有第i-2行

    所以我想的就是(dp[i][a]=sum_{b=0}^{2^m-1}dp[i-1][b]-sum_{c=0}^{2^m-1}dp[i-2][c])

    满足a与b不冲突 b与c不冲突 但是a,与c是冲突的

    也就是减去曾经合法中如今不合法的方案

    后来发现不行

    因为我们不确定dp[i-1][b]中是否包含dp[i-2][c] 有可能会导致多减了

    所以只能改变思路

    只转移绝对合法的

    设置dp[i][a][b] 表示第i行当前行状态为a,上一行状态为b的方案数

    首先我们需要对第一行以及第二行初始化

    然后进行状态转移就可以了

    大体框架是如下的

    for(int a=0;a<(1<<m);++a)//当前行
     for(int b=0;b<(1<<m);++b)//上一行
     {
       if(a与b存在冲突) continue;
       for(int c=0;c<(1<<m);++c)
       {
         if(a与c存在冲突||b与c存在冲突) continue;
         dp[i][a][b]+=dp[i-1][b][c];
       } 
     } 
          
    

    接下来我们需要明白如何判断冲突

    1.两行之间

    一个第j列的棋子会被上一行攻击到

    当且仅当第j-2列以及第j+2列存在马并且没有蹩马腿

    所以我写了这么一个函数

    bool check1(int nowat,int a,int b)
    {
    //nowat是当前判断棋子所在位置(0,1,...,m-1)
    //b是当前行二进制状态
    //a是上一行二进制状态	
    	bool flag=1;
    	if(nowat>=2)//判断左边
    	{//没有攻击他的棋子或者攻击棋子蹩马腿 均不存在      
             //而且攻击是相互的 
    		if(!(((1<<(nowat-2))&a)==0 || (((1<<(nowat-2))&a)!=0&&((1<<(nowat-1))&a)!=0))) flag=0; 
    		if(!(((1<<(nowat-2))&a)==0 || (((1<<(nowat-2))&a)!=0&&((1<<(nowat-1))&b)!=0))) flag=0; 
    	}
    	if(nowat<=(m-1-2))//判断右边
    	{
    		if(!(((1<<(nowat+2))&a)==0 || (((1<<(nowat+2))&a)!=0&&((1<<(nowat+1))&a)!=0))) flag=0; 
    		if(!(((1<<(nowat+2))&a)==0 || (((1<<(nowat+2))&a)!=0&&((1<<(nowat+1))&b)!=0))) flag=0;  
    	}
    	return flag;
    }
    

    2.三行之间

    我们需要判断第j列的棋子是否会被上上行攻击到

    当且仅当上上行第j-1列以及第j+1列没有棋子或者存在棋子被蹩马腿

    所以我又写了这么一个函数

    bool check2(int nowat,int a,int b,int c)
    {
    //c是上上行
    //a是上一行
    //b是当前行	
    	bool flag=1;
    	if(nowat>=1)//判断左边
    	{
    		if(!(((1<<(nowat-1))&c)==0 || (((1<<(nowat-1))&c)!=0&&((1<<(nowat-1))&a)!=0))) flag=0;   
    		if(!(((1<<(nowat-1))&c)==0 || (((1<<(nowat-1))&c)!=0&&((1<<nowat)&a)!=0))) flag=0; 
    	}
    	if(nowat<=(m-1-1))//判断右边
    	{
    		if(!(((1<<(nowat+1))&c)==0 || (((1<<(nowat+1))&c)!=0&&((1<<(nowat+1))&a)!=0))) flag=0; 
    		if(!(((1<<(nowat+1))&c)==0 || (((1<<(nowat+1))&c)!=0&&((1<<nowat)&a)!=0))) flag=0;  
    	}
    	return flag;
    }
    

    好了之后 我们就可以开心的写出总代码了

    注意 由于直接这么开数组 我们是会炸的题面说的也是明明白白了

    由于这道题的转移上每一次都会只涉及到上一行 所以我们可以把第一维也就是行的那一维滚动一下

    CODE:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #define mod 1000000007
    using namespace std;
    int n,m;
    long long dp[2][65][65],ans;
    int ln[100];
    bool check1(int nowat,int a,int b)
    {
    //b是当前行
    //a是上一行	
    	bool flag=1;
    	if(nowat>=2)
    	{
    		if(!(((1<<(nowat-2))&a)==0 || (((1<<(nowat-2))&a)!=0&&((1<<(nowat-1))&a)!=0))) flag=0; 
    		if(!(((1<<(nowat-2))&a)==0 || (((1<<(nowat-2))&a)!=0&&((1<<(nowat-1))&b)!=0))) flag=0; 
    	}
    	if(nowat<=(m-1-2))
    	{
    		if(!(((1<<(nowat+2))&a)==0 || (((1<<(nowat+2))&a)!=0&&((1<<(nowat+1))&a)!=0))) flag=0; 
    		if(!(((1<<(nowat+2))&a)==0 || (((1<<(nowat+2))&a)!=0&&((1<<(nowat+1))&b)!=0))) flag=0;  
    	}
    	return flag;
    }
    bool check2(int nowat,int a,int b,int c)
    {
    //c是上上行
    //a是上一行
    //b是当前行	
    	bool flag=1;
    	if(nowat>=1)
    	{
    		if(!(((1<<(nowat-1))&c)==0 || (((1<<(nowat-1))&c)!=0&&((1<<(nowat-1))&a)!=0))) flag=0; 
    		if(!(((1<<(nowat-1))&c)==0 || (((1<<(nowat-1))&c)!=0&&((1<<nowat)&a)!=0))) flag=0; 
    	}
    	if(nowat<=(m-1-1))
    	{
    		if(!(((1<<(nowat+1))&c)==0 || (((1<<(nowat+1))&c)!=0&&((1<<(nowat+1))&a)!=0))) flag=0; 
    		if(!(((1<<(nowat+1))&c)==0 || (((1<<(nowat+1))&c)!=0&&((1<<nowat)&a)!=0))) flag=0;  
    	}
    	return flag;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=0;i<(1<<m);++i) dp[1][i][0]=1;
    	for(int i=0;i<=6;++i) ln[1<<i]=i;
    	for(int i=2,now=0;i<=n;++i,now^=1)
    	{
    		if(i==2)
    		{
    			for(int a=0;a<(1<<m);++a)//当前行 
    			 for(int b=0;b<(1<<m);++b)//上一行 
    			 {
    			 	bool flag=1;
    			 	for(int x=a;x>0;x-=x&-x)
    			 	{//由于本蒟蒻实在是太辣鸡了 所以不会用什么高级二进制法子 只能乖乖枚举了
    			 		int nowat=ln[x&-x];
    			 		if(!check1(nowat,b,a)) {flag=0;break;}
    				}
    				if(flag) dp[0][a][b]=(dp[0][a][b]+dp[1][b][0])%mod;
    			 }
    		} 
    		else
    		{
    			memset(dp[now],0,sizeof dp[now]);
    			for(int a=0;a<(1<<m);++a)//当前行 
    			 for(int b=0;b<(1<<m);++b)//上一行 
    			 {
    				bool flag1=1,flag2,flag3;
    				for(int x=a;x>0;x-=x&-x)
    				{//同上
    					int nowat=ln[x&-x];
    					if(!check1(nowat,b,a)) {flag1=0;break;}  	
    				}	
    				if(flag1==0) continue;
    				for(int c=0;c<(1<<m);++c)//上上行
    				{
    					flag2=flag3=1;
    					for(int x=b;x>0;x-=x&-x)
    					{//同上
    						int nowat=ln[x&-x];
    						if(!check1(nowat,c,b)) {flag2=0;break;}
    					}
    					if(flag2==0) continue;
    					for(int x=a;x>0;x-=x&-x)
    					{
    						int nowat=ln[x&-x];
    						if(!check2(nowat,b,a,c)) {flag3=0;break;}
    					}
    					if(flag3==0) continue;
    					dp[now][a][b]=(dp[now][a][b]+dp[now^1][b][c])%mod;
    				}	
    			 }  
    		}
    	}
    	for(int i=0;i<(1<<m);++i)
    	 for(int j=0;j<(1<<m);++j)
    	  ans=(ans+dp[n&1][i][j])%mod;
    	printf("%lld
    ",ans);
    	return 0;
    } 
    
  • 相关阅读:
    java中为什么notify()可能会导致死锁,而notifyAll()则不会
    java中wait()和sleep()的区别;notify()和notifyall()区别
    你不知道的Golang盲点汇总【持续更新】
    rsync性能终极优化【Optimize rsync performance】
    基于cephfs搭建高可用分布式存储并mount到本地
    检测代码潜在bug和质量之SonarQube
    玩透二叉树(Binary-Tree)及前序(先序)、中序、后序【递归和非递归】遍历
    好用到哭!8个技巧让Vim菜鸟变专家
    Golang fmt Printf 格式化参数手册/详解/说明
    淘宝滑动验证码研究
  • 原文地址:https://www.cnblogs.com/LovToLZX/p/13924785.html
Copyright © 2011-2022 走看看