蓝桥杯省赛[新手向题解] 2019 第十届 C/C++ A组 第九题 [传送门:暂无]
Problem Description
\(N\)袋糖果,每袋\(K\)颗糖,一共存在\(M\)种糖果。求最少选多少袋糖果可以吃到所有种类糖果。
Input
第一行仨整数\(N\),\(M\)和\(K\)
接下来N行每行\(K\)整数,第\(i\)行表示第\(i\)袋糖有哪几种糖果 (可能两袋内容相同,也可能一袋里有相同种类的糖果,这不重要) (数据范围挺小,大概\(N≤200,M\)和\(k≤20\))Output
输出最小袋数
并不想按格式写:
赛时:
还剩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\) (可以滚动数组)Codes:
咕咕咕~(随便写点,仅供参考)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> a; a = pow(2,a-1); now = (now | a); } for(int j=0; j