csp信奥赛c++之状压枚举

csp信奥赛c++之状压枚举

研究案例:GEPPETTO

题目描述

Geppetto 开了一家披萨店,他正在努力做出全市最好的披萨。

Geppetto 用 N N N 种原材料做比萨,每种原材料只有一个。原材料标号为 1 1 1 到 N N N。做披萨很简单,只要把原材料混合好然后放进烤箱里烤一烤就行了。但 Geppetto 发现一共有 M M M 对原材料是冲突的,如果一对冲突的原材料混合在一份披萨里,这份披萨就会变得十分难吃。这给他带来了额外的麻烦。

Geppetto 想知道他最多能做多少种不同的比萨。如果一份比萨上有编号为 i i i 的原材料,而另一份比萨上没有,那么这两份比萨就是不同的。

输入格式

第一行两个整数 N , M N,M N,M,分别表示原材料总数和冲突总数。

接下来 M M M 行,每行两个整数 x i , y i x_i,y_i xi,yi,表示一对冲突中两种原材料的编号。

输出格式

一行一个整数,表示 Geppetto 最多能做多少种披萨。

输入输出样例 1
输入 1
复制代码
3 2
1 2
2 3
输出 1
复制代码
5
输入输出样例 2
输入 2
复制代码
3 0
输出 2
复制代码
8
输入输出样例 3
输入 3
复制代码
3 3
1 2
1 3
2 3
输出 3
复制代码
4
说明/提示

【样例 1 解释】

Geppetto 可以做出以下 4 种披萨:

1

2

3

1 3

不过因为 Geppetto 可以不放原材料,所以最多可以做出 5 种披萨。

【样例 2 解释】

没有原材料冲突,所以一共可以做出 2 3 = 8 2^3=8 23=8 种披萨。

【样例 3 解释】

由于所有原材料都互相冲突,所以 Geppetto 只能放一种原材料或者不放原材料,一共可以做出 1 + 3 = 4 1+3=4 1+3=4 种披萨。

【数据范围】

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 20 , 0 ≤ M ≤ 400 , 1 ≤ x i , y i ≤ N 1\le N\le 20,0\le M\le 400,1\le x_i,y_i\le N 1≤N≤20,0≤M≤400,1≤xi,yi≤N,保证 x i ≠ y i x_i\ne y_i xi=yi

题目分析

题目要求计算使用 N N N 种原材料制作披萨的方案数,其中原材料编号 1 ∼ N 1 \sim N 1∼N,每种原料最多用一次(即选取一个子集)。有 M M M 对冲突关系,如果一个披萨中同时包含某一对冲突的原材料,则该披萨不可行。需要统计所有可行的披萨种类(包括空披萨,即什么都不放)。

N ≤ 20 N \le 20 N≤20,因此总子集数为 2 N 2^N 2N,直接枚举所有子集并检查冲突是可行的。

思路分析

  1. 将原材料编号映射为 0 ∼ N − 1 0 \sim N-1 0∼N−1,方便位运算处理。
  2. 用一个整数 s s s 的二进制位表示子集:第 i i i 位为 1 1 1 表示选取了原材料 i i i。
  3. 枚举 s s s 从 0 0 0 到 2 N − 1 2^N - 1 2N−1。
  4. 对于每个子集 s s s,检查所有 M M M 对冲突 ( x , y ) (x, y) (x,y):如果 s s s 中同时包含 x x x 和 y y y(即 (s>>x & 1) && (s>>y & 1) 为真),则该子集非法,否则合法。
  5. 统计合法子集个数,即为答案。

时间复杂度 O ( 2 N ⋅ M ) O(2^N \cdot M) O(2N⋅M),最坏约 4 × 10 8 4 \times 10^8 4×108 次操作,C++ 在极限数据下可能略慢,但通常可接受。

代码实现

cpp 复制代码
#include<bits/stdc++.h>   // 万能头文件
using namespace std;

int n, m, a[410], b[410]; // a,b 存储冲突对的两端,数组大小根据 M≤400 开够

int main() {
    // 输入原材料数 n 和冲突对数 m
    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        cin >> a[i] >> b[i];
        // 将编号转换为 0~n-1,方便位运算
        --a[i];
        --b[i];
    }

    int ans = 0; // 答案计数
    // 枚举所有子集,s 从 0 到 (1<<n)-1
    for (int s = 0; s < (1 << n); ++s) {
        bool ok = true; // 标记当前子集是否合法
        // 检查所有冲突对
        for (int i = 0; i < m; ++i) {
            // 如果子集中同时包含 a[i] 和 b[i] 这两个原料
            if ((s >> a[i] & 1) && (s >> b[i] & 1)) {
                ok = false; // 非法,退出检查
                break;
            }
        }
        if (ok) ++ans; // 合法则计数
    }

    cout << ans << endl;
    return 0;
}

功能分析

  • 算法原理:暴力枚举 + 冲突检测。利用二进制位表示子集,通过位运算快速判断某个元素是否在子集中。
  • 时间复杂度 : O ( 2 N ⋅ M ) O(2^N \cdot M) O(2N⋅M),当 N = 20 , M = 400 N=20, M=400 N=20,M=400 时,约为 4 × 10 8 4 \times 10^8 4×108 次检查,实际运行在评测环境下可接受。
  • 空间复杂度 : O ( M ) O(M) O(M),仅存储冲突对。
  • 适用性 : N N N 较小(≤20)时非常有效;若 N N N 更大则需考虑其他方法(如 DP、容斥等)。
  • 边界情况
    • M = 0 M=0 M=0:无冲突,答案为 2 N 2^N 2N。
    • 冲突对可能重复?题目未说明,但即使重复也不影响结果(重复检查不影响正确性,可优化去重但没必要)。
    • 空集( s = 0 s=0 s=0)总是合法。

各种学习资料,助力大家一站式学习和提升!!!

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"##########  一站式掌握信奥赛知识!  ##########";
	cout<<"#############  冲刺信奥赛拿奖!  #############";
	cout<<"######  课程购买后永久学习,不受限制!   ######";
	return 0;
}

【秘籍汇总】(完整csp信奥赛C++学习资料):

1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):

https://edu.csdn.net/lecturer/7901 点击跳转

2、CSP信奥赛C++竞赛拿奖视频课:

https://edu.csdn.net/course/detail/40437 点击跳转

https://edu.csdn.net/course/detail/41081 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html 点击跳转

4、csp信奥赛冲刺一等奖有效刷题题解:

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新): https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13125089.html 点击跳转

5、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html 点击跳转

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
田梓燊5 小时前
力扣:23.合并 K 个升序链表
算法·leetcode·链表
re林檎6 小时前
算法札记——4.27
算法
AI人工智能+电脑小能手6 小时前
【大白话说Java面试题】【Java基础篇】第15题:JDK1.7中HashMap扩容为什么会发生死循环?如何解决
java·开发语言·数据结构·后端·面试·哈希算法
数据牧羊人的成长笔记7 小时前
逻辑回归与Softmax回归
算法·回归·逻辑回归
郑州光合科技余经理7 小时前
同城O2O海外版二次开发实战:从支付网关到配送算法
开发语言·前端·后端·算法·架构·uni-app·php
张健11564096488 小时前
使用信号量限制并发数量
开发语言·c++
jc06208 小时前
6.1云原生之Docker
c++·docker·云原生
Mrlxl.cn9 小时前
计算机网络——网络层
c语言·数据结构·计算机网络·考研
d111111111d10 小时前
STM32-UART封装问题解析
笔记·stm32·单片机·嵌入式硬件·学习·算法
寒秋花开曾相惜10 小时前
(学习笔记)4.2 逻辑设计和硬件控制语言HCL(4.2.1 逻辑门&4.2.2 组合电路和HCL布尔表达式)
linux·网络·数据结构·笔记·学习·fpga开发