[SCOI2005] 互不侵犯
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1087
题目大意: 给出一个n*n的棋盘,要求在上面摆K个国王,互相不攻击,求多少种方法。
分析:
状压DP第一发!
一看n<=9,大致暴搜思路以明确。差不多分析下,用F[i][j][S]表示填了i行,用了j个国王,当前行状态为S(01串)。
枚举当前行的状态,上一行找与当前行不冲突的状态,
加一下F[i-1][j-当前行所用国王数][S0]
优化(1):先DFS所有一行中合法的状态,而不是先用现判。
优化(2):先判断一下任意两种状态之间矛盾与否,转移时就不用判断了。
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
using namespace std;
const int maxn=10;
typedef long long LL;
int n,h,K,S[1<<maxn],num[1<<maxn],match[1<<maxn][1<<maxn],size;
LL F[maxn][maxn*maxn][1<<maxn],ans;
void DFS(int x,int s) //找一行中的合法状态
{
 if (x==n) {S[++size]=s;return;}
 DFS(x+1,s<<1);
 if (!(s&1)) DFS(x+1,(s<<1)+1);
}
int main()
{
 scanf("%d%d",&n,&K);
 
 size=0;
 DFS(0,0);
 
 rep(i,1,size)
 {
  num[i]=0;
  rep(j,0,n-1) if (S[i]&(1<<j)) num[i]++; //计算一个行状态用了几个国王
 }
 rep(i,1,size)
  rep(j,1,size)
  {
   h=0;
   rep(l,0,n-1)
   if (S[i]&(1<<l))
   {
    if (l>0) h=h|(1<<(l-1));
    if (l<(n-1)) h=h|(1<<(l+1));
    h=h|(1<<l); 
   }
   if (h&S[j]) match[i][j]=0; else match[i][j]=1; //判断是否矛盾
  }
 
 F[0][0][1]=1;
 rep(i,1,n)
  rep(j,0,K)
   rep(l,1,size)
   {
    F[i][j][l]=0;
    if (j<num[l]) continue; //当前状态国王太多
    rep(h,1,size)
    {
     if ((num[h]+num[l])>j) continue; //国王太少
     if (match[h][l]) F[i][j][l]+=F[i-1][j-num[l]][h];
    }
   }
 
 ans=0;
 rep(i,1,size) ans+=F[n][K][i];
 printf("%lld\n",ans);
 return 0;
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号