[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;
}

posted @ 2016-07-06 16:54  Krew  阅读(142)  评论(0)    收藏  举报