??\(N\)袋糖果,每袋\(K\)颗糖,一共存在\(M\)种糖果。求最少选多少袋糖果可以吃到所有种类糖果。
??第一行仨整数\(N\),\(M\)和\(K\)
??接下来N行每行\(K\)整数,第\(i\)行表示第\(i\)袋糖有哪几种糖果
??(可能两袋内容相同,也可能一袋里有相同种类的糖果,这不重要)
??(数据范围挺小,大概\(N≤200,M\)和\(k≤20\))
??输出最小袋数
??还剩40分钟的分钟的时候摸到最后两题,感觉这题希望比较大,遂果断最后一题骗分再回来写这题。结果这题从12:35开始想算法到12:50写完……果然还是DP爽。
??每种味道存在或不存在,一共\(2^{M}=2^{20}=1024^2\)种状态,直接状压DP
感觉还是直接上??比较好解释:
??比如\(M=6,K=3\),一共有\(2^6=64\)种状态,用二进制表示分别为:
????\(0=(000000)_2\) 表示所有糖果都没有
????\(1=(000001)_2\) 表示只有第1种糖果
????\(2=(000010)_2\) 表示只有第2种糖果
????\(3=(000011)_2\) 表示有第1种和第2种糖果
????......
????\(63=(111111)_2\) 表示所有糖果都有
????\(dp[i][j]\)表示搜索到第\(i\)袋的时候,达到状态\(j\)最少需要多少袋,最终需要的答案是\(dp[N][2^{M}]\)
????比如当前搜索到第\(4\)袋的内容为\(1,3,4\),对应的状态为\((001101)_2=13\),对于前3袋处理过的任意状态,比如有第3种和第5种糖果的状态\((010100)_2=20\),如果把两者结合,就会转移到有第\(1,3,4,5\)种糖果的状态\((011101)_2=29\),这里的运算是按位或(|),也就是只要两者其中之一包含第\(i\)种糖果,合并的状态中就包含第\(i\)种糖果。按这个例子来说就是\(dp[3][20]\)根据输入的001101转移到了\(dp[4][29]=dp[3][20]+1\)。
????要注意,\(dp[4][29]\)可能不止由\(dp[3][20]\)转移过来,我们要保留所有转移方案中袋数最小的,所以应该是\(dp[4][29] = min(dp[4][29],dp[3][20]+1)\)。
????同时,如果放弃第4袋糖果,就有\(dp[4][20]=dp[3][20]\),即没有产生影响。
??对于第\(i\)袋糖果\(a_{i_1},a_{i_2}......a_{i_K}\),它的状态为\(p[i]=2^{a_{i_1}-1}\ |\ 2^{a_{i_2}-1}\ |\ ......\ |\ 2^{a_{i_K}-1}\),这里涉及二进制的转换,第\(i\)种糖果在二进制的右边第\(i\)位,它的十进制为\(2^{i-1}\),然后把每个糖果贡献加起来就是这袋的总贡献。但是,一袋糖里可能会有相同的,所以用加法就会出错,不过按位或就不会,因为1和1作按位或,结果仍然是1,即重复的糖果不影响结果。
??状态转移方程:
\[dp[i+1][j|p[i]] = min(dp[i+1][j|p[i]],dp[i][j] + 1)\]
??初始状态:
????到达没有糖的状态不需要任何袋数,\(dp[i][0]=0\)
????第0袋的时候,到达任何状态的最小袋数都是无穷大,即\(dp[0][j] = INF\)
??(可以滚动数组)
咕咕咕~
(随便写点,仅供参考)
const int MAXN = 1024*1024;
int dp[MAXN];
int N,M,K;
int main()
{
cin >> N >> M >> K;
int MAXM = pow(2,M);
memset(dp,0x3f3f,sizeof(dp));
dp[0] = 0;
while(N--)
{
int now(0);
for(int i=0; i<K; ++i)
{
int a;
cin >> a;
a = pow(2,a-1);
now = (now | a);
}
for(int j=0; j<MAXM; ++j)
dp[(j | now)] = min(dp[(j | now)], dp[j] + 1);
}
cout << dp[MAXM-1] << endl;
return 0;
}
蓝桥杯省赛[新手向题解] 2019 第十届 C/C++ A组 第九题 DP
原文:https://www.cnblogs.com/MMMMMMing/p/10611950.html