[SCOI2005]互不侵犯King

题目描述

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

    ——by洛谷

    https://www.luogu.org/problem/show?pid=1896



   显然的棋盘模型的状压DP,故考虑对每行二进制状压,状压为表示有无王的01串,再由题意得到如下的限制:

  • 每个串中不存在连续两个以上的1
  • 相邻两串的同列不存在同为1的情况
  • 不存在与上一行左侧或右侧同为1的情况

    于是对于限制一,可在预处理中实现;对于二三,可在转移时限制,得出状态转移方程:

    f[i][j][l+c[j]]=Σf[i-1][k][l];(i:当前行数;j:枚举本行所有可能状态,l+c[j]:此时的王的个数,c[j]:状态s[j]中王的个数)

然后对k进行限制,s[j]&s[k]==0,s[j]&(s[k]<<1)==0,s[j]&s[k]>>1)==0;之类的

    需要注意的是:

  • dfs预处理
  • 处理好c[j]

总结:简单的DP么

代码如下:

#include<cstdio>
using namespace std;
long long f[10][1000][100];
int s[100],c[100];
int man;
int n,m;
void dfs(int ,int ,int ,int );
int main()
{
    int i,j,k,l;
    long long ans=0;
    scanf("%d%d",&n,&m);
    dfs(1,0,0,0);
        f[0][1][0]=1;
    for(i=1;i<=n;i++)
        for(j=1;j<=man;j++)
            for(k=1;k<=man;k++)
            if((s[j]&s[k])==0&&(s[j]&(s[k]<<1))==0&&(s[j]&(s[k]>>1))==0)
                for(l=c[k];l<=m-c[j];l++)
                    f[i][j][l+c[j]]+=f[i-1][k][l];
    for(i=1;i<=man;i++)
        ans+=f[n][i][m];
    printf("%lld",ans);
    return 0;
}

void dfs(int now,int num,int ma,int p)
{
    int i;
    if(ma>m)return;
    if(now==n+1)
    {
        s[++man]=num;
        c[man]=ma;
        return;
    }
    for(i=0;i<=1;i++)
    if(1-p>=i)
        dfs(now+1,num+i*(1<<(now-1)),ma+i,i);
}

祝AC哟;

Just close your eyes, you`ll be alright, no one can hurt you after you die.
原文地址:https://www.cnblogs.com/nietzsche-oier/p/6160219.html