位掩码
左移运算<< 箭头的符号表示 向左移动并且添0。
口味编号的输入
合法状态 :循环的条件为i<(1<<m)
较大的整数值不能用memset来设置
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const ll inf=2e18;
const int N=1e2+9;
ll a[N],dp[1<<21];
int n,m,k;
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)//每一包糖果
{
for(int j=1;j<=k;j++)//一包糖果的口味集合是每颗糖组成的
{
int x;cin>>x;
a[i]|=1<<(x-1);
}
}
for (int sta = 1; sta < (1 << m); ++sta) dp[sta]=inf;
for(int sta=0;sta<(1<<m);sta++)
{//对于所有的口味
for(int i=1;i<=n;i++)//对于每一包糖果
{
dp[sta|a[i]]=min(dp[sta|a[i]],dp[sta]+1);
}
}
ll ans=dp[(1<<m)-1];
cout<<(ans==inf?-1:ans);
return 0;
}
位运算中注意编号和位的关系
第一位(从右往左)编号为0;第二编号为1;第x位编号为x-1
1<<0结果为000001 第1位编号为0 ;1<<1结果为0000010第二位编号为1
与运算,只有都为1的时候结果为1
算法讲解003【入门】二进制和位运算_哔哩哔哩_bilibili
负数和二进制相互转化:
负数转化二进制:负数先看成正数,然后-1,然后每一位取反
相反数:取反之后+1得到相反数
对于非负数左移和右移的效果就是添加几个0;
如1<<3 = 1000
状态转移方程的解释
其他方案对最终方案的辅助作用:
//末尾有状态解释
// dp[sta] 表示在状态 sta 下,小明已经品尝到的糖果口味的集合的最小购买数量
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 105;
const ll inf = 2e18; //无穷大
ll a[N], dp[1<<21]; // 每包糖果的口味位掩码
//通过位掩码来表示糖果被品尝的状态,
// 这个题目种只有20种糖果,初始化有21位,这样能够通过每一位是否为1来表示物品是否被选择
// 即糖果是否被品尝到
int main() {
int n, m, k; // 读取糖果包数量n,口味种类m,每包糖果数量k
cin >> n >> m >> k;
for (int i = 1; i <= n; ++i) { // 遍历每包糖果
for (int j = 1; j <= k; ++j) { // 遍历每包糖0果中的每种口味
int x; // 读取第j种口味的编号
cin >> x;
a[i] |= (1 << (x - 1)); /*使用位运算将第j种口味添加到第i包糖果的位掩码中
左移就是在数的右边添加几个零
这样原来的数就会在第四位上,但是为了表示编号,所以要-1
如 a[i]<<3表示第3种口味输入了,但是他会在第四位上
1000,想让他成为100在第三位上,就要写成a[i]<<3-1;
然后再取或运算添加到目标的数上
*/
}
}
for (int sta = 1; sta < (1 << m); ++sta) dp[sta]=inf; // 初始化dp数组,除了dp[0]外,其他状态的初始值为无穷大
for (int sta=0;sta<(1<<m);sta++) {
//为什么这里的初始条件设置为<(1<<m)?
for (int i = 1; i <= n; ++i) { // 遍历每包糖果
dp[sta|a[i]]=min(dp[sta|a[i]],dp[sta]+1); // 状态转移:如果通过购买第i包糖果可以从状态sta转移到状态sta|a[i],则更新dp[sta|a[i]]
// 在购买第i包糖果得到的新组合可以由旧状态+1得到,
//也可能在购买别的包(比如第j包)的时候就已经得到,所以要比较这两种情况的最小购买次数
// 作为新方案的购买次数
}
// dp[sta|a[i]]通过购买糖果之后小明可以尝到的口味合集
//在当前状态 sta 下,
// 小明购买第 i 包糖果后,品尝到的新口味的集合的最小购买数量加1。
}
// 最后只是需要全部吃到的方案,为什么还要求其他品尝方案的最小购买量?
// 对目标结果有什么影响?
ll ans = dp[(1 << m) - 1];
cout << (ans == inf ? -1 : ans) << '\n';
return 0;
// 最后检查是否存在全品尝完的情况
// 即全部买完之后能够品尝到全部种类的糖果
}