PS:如果读过题了可以跳过题目描述直接到题解部分
提交链接:洛谷 P7859 [COCI2015-2016#2] GEPPETTO
题目
题目描述
Geppetto 开了一家披萨店,他正在努力做出全市最好的披萨。
Geppetto 用 N 种原材料做比萨,每种原材料只有一个。原材料标号为 1 到 N。做披萨很简单,只要把原材料混合好然后放进烤箱里烤一烤就行了。但 Geppetto 发现一共有 M 对原材料是冲突的,如果一对冲突的原材料混合在一份披萨里,这份披萨就会变得十分难吃。这给他带来了额外的麻烦。
Geppetto 想知道他最多能做多少种不同的比萨。如果一份比萨上有编号为 i 的原材料,而另一份比萨上没有,那么这两份比萨就是不同的。
输入格式
第一行两个整数 N,M,分别表示原材料总数和冲突总数。
接下来 M 行,每行两个整数 x[i],y[i],表示一对冲突中两种原材料的编号。
输出格式
一行一个整数,表示 Geppetto 最多能做多少种披萨。
样例1
样例输入
3 2
1 2
2 3
样例输出
5
样例2
样例输入
3 0
样例输出
8
样例3
样例输入
3 3
1 2
1 3
2 3
样例输出
4
提示
样例 1 解释
Geppetto 可以做出以下 4 种披萨:
1
2
3
1 3
不过因为 Geppetto 可以不放原材料,所以最多可以做出 5 种披萨。
样例 2 解释
没有原材料冲突,所以一共可以做出 2^3=8 种披萨。
样例 3 解释
由于所有原材料都互相冲突,所以 Geppetto 只能放一种原材料或者不放原材料,一共可以做出 1+3=4 种披萨。
数据范围
对于 100% 的数据,1≤N≤20,0≤M≤400,1≤x[i],y[i]≤N,保证 x[i]≠y[i]。
题解
位运算
位运算是这道题的前置知识点,借此机会我就把常用的位运算都总结在这里了。
位运算是基于二进制的基础上的一种运算方式。
左移(<<)
在二进制的基础上左移一位,相当于乘2。
例如:1101<<1=11010
右移(>>)
在二进制的基础上右移一位,相当于除以2(下去整)。
例如:1101>>1=110
或运算(|)
只要有一个为1,则为1。
例如:1100|1010=1110
与运算(&)
两个都为1才为1,只要有一个为0,则为0。
例如:1100&1010=1000
异或运算(^)
同为0,异为1。
例如:1100^1010=0110
非运算(~)
取反。
例如:~1010=0101
状压DP
考点,正解。
但我不是这么做的
状压暴搜
对,虽然说这道题的考点在状压DP,但因为数据范围确实不大,所以我用的方法是状压暴搜。(我自己取的名字)
就是把 n 种材料用或者不用看做一种 1 或 0 的状态,压缩成一个二进制数。只要从 0 开始一直搜到 2^n-1 就好了。
具体过程不太好描述,直接看代码就懂了。
代码实现
//洛谷 P7859 [COCI2015-2016#2] GEPPETTO
#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
bool b;
long long ans;
struct node{
int x,y;
}a[410];
int main(){
scanf("%d%d",&n,&m);
if(m==0){//既然没有要求就直接输出吧
printf("%lld",(1<<n));
return 0;
}
for(int i=1;i<=m;++i){
scanf("%d%d",&a[i].x,&a[i].y);
}
for(int i=0;i<(1<<n);++i){//是状压暴搜鸭
b=0;//记得归零
for(int j=1;j<=m;++j){
if((i&(1<<a[j].x-1))&&(i&(1<<(a[j].y-1)))){//两种材料冲突啦
b=1;
break;
}
}
if(b){
continue;
}
++ans;
}
printf("%lld\n",ans);
return 0;
}