洛谷P1896[SCOI2005]互不侵犯(状压DP)

题目描述

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

注:数据有加强(2018/4/25)

输入输出格式

输入格式:

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

输出格式:

所得的方案数

输入输出样例

输入样例#1:

 3 2

输出样例#1:

 16


(Solution:)

(n<=9),所以我们可以用状压DP解决这题

由于当前状态只会对下一行造成影响,所以DP方程中要记一下这个状态,状压一下就好了;另外你只能放 (k) 个国王,所以你还需要一维来记录当前放的国王总数。

首先我们可以处理出每一行所有可能的状态,记下状态并记下放了多少国王,这个可以用DFS实现:

void dfs(int p,int v,int c){
    if(p>=n)
    {	
        //h[i]数组表示第i种可能的情况中放了多少国王
        //g[i]表示第i种可能的情况的状态
        h[++cnt]=v,g[cnt]=c;
        return;
    }//这里我把可能状态的总数用cnt记录了下来
    dfs(p+1,v,c);//如果不在p号位置放国王,就继续找
    dfs(p+2,v+1,c+(1<<p));//如果在p号位置放,那么p+1号位置就放不了了
}

(f[i][j][k]) 表示已经到了第 (i) 行,总共放了 (j) 个国王,且当前状态为(k) 种可能状态的方案数。转移方程就是:

for(int i=1;i<=cnt;i++) f[1][h[i]][i]=1;//初始化第一行
for(int i=2;i<=n;i++)
    for(int j=1;j<=cnt;j++)//枚举可能的状态编号
        for(int k=1;k<=cnt;k++)//同上
        {
            if(g[j]&g[k]) continue;
            if(g[j]&(g[k]>>1)||g[j]&(g[k]<<1))
                continue;//判断合法性
            for(int p=m;p>=h[j];p--)//转移
                f[i][p][j]+=f[i-1][p-h[j]][k];
        }

最后附上总代码:

#include<bits/stdc++.h>
#define il inline
#define int long long//这题要开long long
using namespace std;
const int N=1100;

il int read(){
    int f=1,w=0;char c=0;
    while(!isdigit(c))
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c)) w=w*10+(c^48),c=getchar();
    return f*w;
}

int n,m,cnt,h[N],g[N],f[10][100][N],ans;

void dfs(int p,int v,int c){
    if(p>=n)
    {
        h[++cnt]=v,g[cnt]=c;
        return;
    }
    dfs(p+1,v,c);
    dfs(p+2,v+1,c+(1<<p));
}

main(){
    n=read(),m=read();
    dfs(0,0,0);//处理每一行的可能的状态
    for(int i=1;i<=cnt;i++) f[1][h[i]][i]=1;
    for(int i=2;i<=n;i++)
        for(int j=1;j<=cnt;j++)
            for(int k=1;k<=cnt;k++)
            {
                if(g[j]&g[k]) continue;
                if(g[j]&(g[k]>>1)||g[j]&(g[k]<<1))
                    continue;
                for(int p=m;p>=h[j];p--)
                    f[i][p][j]+=f[i-1][p-h[j]][k];
            }
    //DP过程
    for(int i=1;i<=cnt;i++) ans+=f[n][m][i];//计算答案
    cout<<ans<<endl;
    return 0;
}

原文地址:https://www.cnblogs.com/ajy-shi-cj-zui-cai/p/10387904.html