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;
}
相关推荐
wayz112 小时前
数据分析中的异常值处理:MAD
算法·数据挖掘·数据分析
飞Link2 小时前
LangGraph 核心架构解析:节点 (Nodes) 与边 (Edges) 的工作机制及实战指南
java·开发语言·python·算法·架构
Mr_Xuhhh3 小时前
深入理解二叉树:从数据结构到算法实战
数据结构·算法
xuhaoyu_cpp_java3 小时前
Boyer-Moore 投票算法
java·经验分享·笔记·学习·算法
kobesdu3 小时前
FAST-LIO2 + 蓝海M300激光雷达:从建图到实时栅格图的完整流程
算法·机器人·ros·slam·fast lio
x_xbx3 小时前
LeetCode:438. 找到字符串中所有字母异位词
算法·leetcode·职场和发展
MThinker3 小时前
K230+canMV+micropython实现低成本MLX90640红外热成像测温模块(续)
算法·智能硬件·micropython·canmv·k230
小菜鸡桃蛋狗3 小时前
C++——string(下)
算法
学习永无止境@3 小时前
灰度图像中值滤波算法实现
图像处理·算法·计算机视觉