【[SCOI2005] 互不侵犯】【状压DP(含概念讲解)】

什么是状压DP

状压 DP 是动态规划的一种,通过将状态压缩为整数来达到优化转移的目的。
在这里插入图片描述

如下图所示,可以用0/1表示状态,然后转换为十进制数进行存储,大大减少存储空间。

在这里插入图片描述

[SCOI2005] 互不侵犯

题目连接

题目描述

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

输入格式

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

输出格式

所得的方案数

样例 #1

样例输入 #1

3 2

样例输出 #1

16

思路分析

数组f[i][st][j]的定义,以及状态转移方程:

在这里插入图片描述

不合法检验:

行内不能相邻,行间要检验三位。

在这里插入图片描述

st 的范围(状态数组的大小):

在这里插入图片描述

代码

int n, k;
ll f[9][1 << 9][82], ans;
int c(int st){
    //返回st的二进制中1的个数
    int cnt = 0;
    while(st){
        if(st%2)
            cnt++;
        st /= 2;
    }
    return cnt;
}
bool check1(int st){
	//同行检验
    for (int i = 0; i < n - 1;i++)
        if ((st & (1 << i)) && (st & (1 << (i + 1))))
            return false;
    return true;
}
bool check2(int st,int st2){
    //与上一行的检验
    for (int i = 0; i < n;i++){
        if (st & (1 << i)){
            if (st2 & (1 << i))
                return false;
            else if (i + 1 < n && (st2 & (1 << (i + 1))))
                return false;
            else if (i - 1 < n && (st2 & (1 << (i - 1))))
                return false;
        }
    }
    return true;
}
void solve(){
    cin >> n >> k;
    for (int i = 0; i < n;i++){
        for (int st = 0; st < (1 << n);st++){
            if(!check1(st))
                continue;
            if (i == 0)
                f[i][st][c(st)] = 1;
            else{
                for (int j = c(st); j <= k;j++){
                    for (int st2 = 0; st2 < (1 << n);st2++){
                        if(!check1(st2)||!check2(st,st2))
                            continue;
                        f[i][st][j] += f[i - 1][st2][j - c(st)];
                    }
                }
            }
        }
    }
    for (int st = 0; st < (1 << n);st++)
        ans += f[n - 1][st][k];
    cout << ans << nl;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eternity_GQM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值