题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4529
 
题目意思:
有一8*8的矩阵,放了8个皇后,让你放n个骑士,使骑士之间不能相互攻击(走日字)。
解题思路:
状态压缩 dp.
dp[i][j][p][q]:表示第i行状态为q(q写成二进制时,1表示放骑士,0表示不放骑士),第i-1行状态为p,并且前i行总的骑士的个数为j时总的方案总数。
对每一行,枚举每一种状态,依据前两行的状态,进行转移。
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;
#define ll __int64
#define Maxn 60
ll dp[2][11][1<<8][1<<8]; //dp[i][j][p][q] 表示第i行放马的状态为q,
                  //第i-1行放马的状态为p,前i行放马的总个数为j个的总方案数
bool flag1[1<<8][1<<8],flag2[1<<8][1<<8];
bool iscan(int a,int b,int d) //判断状态a和状态b之差是不是相隔d列的
{
   for(int i=0;i+d<8;i++)
   {
      int x=1<<i;
      int y=1<<(i+d);
      if((a&x)&&(b&y))
         return false;
      if((b&x)&&(a&y))
         return false;
   }
   return true;
}
int num[11];//统计每一行中皇后所在位置的状态
int cnt[1<<8]; //每种状态中1的个数
char save[11];
int cal(int x) //统计该状态下含1的个数
{
   int res=0;
   while(x)
   {
      res+=(x&1);
      x=x>>1;
   }
   return res;
}
int main()
{
   for(int i=0;i<(1<<8);i++)
   {
      for(int j=0;j<(1<<8);j++)
      {
         flag1[i][j]=iscan(i,j,1); //预处理下相隔为1的情况
         flag2[i][j]=iscan(i,j,2); //预处理下相隔为2的情况
      }
      cnt[i]=cal(i);
   }
   int t,n;
   scanf("%d",&t);
   while(t--)
   {
      scanf("%d",&n);
      for(int i=0;i<8;i++)
      {
         scanf("%s",save);
         num[i]=0;
         for(int j=0;j<8;j++) //计算皇后的位置状态
         {
            num[i]<<=1;
            if(save[j]=='*')
               num[i]++;
         }
      }
      memset(dp,0,sizeof(dp));
      //先预处理第一行的情况
      for(int i=0;i<(1<<8);i++)
      {
         if(cnt[i]>n||(num[0]&i)) //有皇后的位置不能放
            continue;
         dp[0][cnt[i]][0][i]++;
      }
      int cur;
      for(int i=1;i<8;i++) //滚动数组,注意每一步都要清零
      {
         cur=i&1;
         for(int j=0;j<(1<<8);j++)
            for(int k=0;k<(1<<8);k++)
               for(int nn=0;nn<=n;nn++)
                  dp[cur][nn][j][k]=0;
         for(int j=0;j<(1<<8);j++)
         {
            if((i-2)>=0&&(num[i-2]&j)) //不能放
               continue;
            for(int k=0;k<(1<<8);k++)
            {
               if(num[i-1]&k)  //不能放
                  continue;
               if(cnt[j]+cnt[k]>n)
                  continue;
               if(flag2[j][k]==false)
                  continue;
               int tt=cnt[j]+cnt[k];
               for(int p=tt;p<=n;p++) //j表示i-2行的状态,k表示i-1行的状态
               {
                  if(dp[cur^1][p][j][k]==0) //前面两行就不能满足了
                     continue;
                  for(int z=0;z<(1<<8);z++) //枚举当前行的状态,建立转移方程
                  {
                     if(!flag2[k][z]||!flag1[j][z])
                        continue;
                     if(z&num[i])
                        continue;
                     if(p+cnt[z]>n)
                        continue;
                     dp[cur][p+cnt[z]][k][z]+=dp[cur^1][p][j][k];
                  }
               }
            }
         }
      }
      ll ans=0; //统计出答案
      for(int i=0;i<(1<<8);i++)
         for(int j=0;j<(1<<8);j++)
            ans+=dp[cur][n][i][j];
      printf("%I64d\n",ans);
   }
   return 0;
}
 
                   
                   
                   
                   
                             本文介绍了一道经典的国际象棋问题——放置骑士与皇后的组合问题。利用状态压缩DP方法,通过预处理不同状态间的冲突情况,实现高效的方案计数。
本文介绍了一道经典的国际象棋问题——放置骑士与皇后的组合问题。利用状态压缩DP方法,通过预处理不同状态间的冲突情况,实现高效的方案计数。
           
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                   410
					410
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            