互不侵犯

【题目描述】

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

【输入描述】

只有一行,包含两个数N、K(1 <= N <= 9,0 <= K <= N*N)。

【输出描述】

输出一行,包含一个整数,表示方案数。

【样例输入】

3 2

【样例输出】

16

50分暴搜(暴力都不会写,我真是弱得无人能及!):

源代码:

#include<cstdio>
int n,k,ans(0),x[8]={1,1,1,0,0,-1,-1,-1},y[8]={-1,0,1,1,-1,-1,0,1};
bool f[10][10];
void DFS(int X,int Y,int S) //如下图,一行接一行地搜索,避免重复,值得借鉴。
{
    if (S==k)
    {
        ans++;
        return;
    }
    if ((n-X)*n+(n-Y)<=(k-S)) //A*优化。
      return;
    for (int a=X;a<=n;a++)
      for (int b=1;b<=n;b++)
        if (!f[a][b]&&(a>X||b>Y))
        {
            bool t(0);
            for (int c=0;c<8;c++)
              if (f[a+x[c]][b+y[c]]) //值得借鉴,周围有国王就不成立。
              {
                t=true;
                break;
              }
            if (!t)
            {
                f[a][b]=true;
                DFS(a,b,S+1);
                f[a][b]=false;
            }
        }
}
int main() //Re:从制杖开始的沙茶生活。
{
    scanf("%d%d",&n,&k);
    for(int a=1;a<=n;a++)
      for(int b=1;b<=n;b++)
      {
        f[a][b]=true;
        DFS(a,b,1);
        f[a][b]=false;
      }
    printf("%d",ans);
    return 0;
}

/*
    *****
    *****
    **#..
    .....
    .....
    设当前点为"#",则搜索区域为"."。
*/

状态压缩动态规划(正解):

源代码:

#include<cstdio>
long long f[10][101][512],Ans; //注意数据类型。
int M,N,K,Stay[101],Num[101]; //Stay[]记录每种状态压缩后的值,Num[]记录对应的状态中1的个数。
bool Map[101][101];
void DFS(int k,int Place,int Now) //k是放了几颗,Place是当前放的位置,Now是当前状态压缩后的值。
{
    Stay[++M]=Now;
    Num[M]=k;
    if (k>=(N+1)/2||k>=K) //边界剪枝。
      return;
    for (int a=Place+2;a<=N;a++) //国王不相邻。
      DFS(k+1,a,Now+(1<<(a-1))); //注意,状态是颠倒的。
}
void Init()
{
    DFS(0,-1,0); //预处理出一行的每种状态,共有M种。
    for (int a=1;a<=M;a++)
      for (int b=1;b<=M;b++)
        Map[a][b]=Map[b][a]=((Stay[a]&Stay[b])||((Stay[a]>>1)&Stay[b])||((Stay[a]<<1)&Stay[b]))?0:1; //预处理状态共存情况。
    for (int a=1;a<=M;a++)
      f[1][Num[a]][a]=1; //预处理边界。
}
int main() //状态压缩DP。
{
    scanf("%d%d",&N,&K);
    Init();
    for (int a=2;a<=N;a++) //行数,1为边界,从2开始转移。
      for (int b=0;b<=K;b++) //棋子数。
        for (int Now=1;Now<=M;Now++) //这一行的情况。
        {
            if (Num[Now]>b) //大于所给棋子数,跳过。
              continue;
            for (int k=1;k<=M;k++) //枚举上一行的状态。
              if (Map[k][Now]&&Num[k]+Num[Now]<=b)
                f[a][b][Now]+=f[a-1][b-Num[Now]][k]; //方案数转移。
        }
    for (int a=1;a<=M;a++) //总数统计。
      Ans+=f[N][K][a];
    printf("%lld",Ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Ackermann/p/5773908.html