Question:
Solve:
核心:
状态压缩二进制存数
枚举尝试拿糖果
dp得出最后结果
首先考虑数据存放,最直接的肯定是二维数组,bool a[i][j],第i包糖果是否含有第j种糖果
很明显,这样放空间占用大,而且数组遍历浪费时间
因为最大的糖果种数也就20,且每包糖果里每种糖果只有有或者没有两种状态
所以可以使用二进制来存放,比如0101表示含有第一种和第三种糖果,那所谓的拿到所有种类糖果也就是二进制数所有位数都为1,这样存放还有一个什么好处呢,比如我取了两包糖果,一包10100,另一包00101,那我现在所拥有的糖果种类就可以表示为两数按位或 10100 | 00101 = 10101
思考到这里以后再想,怎么保证得到的结果是最小呢,那就只有枚举了
在枚举尝试每一包的糖果的时候,我可以去用dp[xxxxxxx] = v来表示拿到xxxxxxx中所含糖果需要拿的最小包数为v, 并通过不断刷新v来得到最后结果(二进制是xxxxxxx,但是dp实际的下标1肯定是要用十进制表示的哈)
想到这一步之后问题也就解决了,结果自然就是dp[(1<<m)-1]了,m种糖果,(1<<m)-1不就是m个数位都是1所表示的十进制数吗?
Code:
#include <bits/stdc++.h>
using namespace std;
int n, m, k, x;
int a[110];
int dp[1<<21];
int main(void)
{
cin >>n >>m >>k;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= k; j++){
//读入每包糖果
cin >>x;
a[i] |= 1 << (x - 1);
}
}
//dp初始化
//因为是不断刷新最小值,所以初始化应该是最大值
memset(dp, 127, sizeof(dp));
dp[0] = 0;
for(int i = 1; i <= n; i++){
for(int j = 0; j < (1 << m); j++){
//如果最小次数已经大于n,跳过
if(dp[j] > n) continue;
//将原来得到糖果所需的次数与当前次数比较并赋值
dp[j | a[i]] = min(dp[j | a[i]], dp[j] + 1);
}
}
//如果拿到所有糖果所需次数大于n(其实应该是因为某种糖果没有),输出-1
if(dp[(1<<m)-1] > n) cout <<-1;
else cout <<dp[(1<<m)-1];
return 0;
}
最后附上蓝桥杯汇总链接:蓝桥杯C/C++A组省赛历年真题题解
声明:图片均来源于蓝桥杯官网,以个人刷题整理为目的,如若侵权,请联系删除~