首页 > 编程语言 > 详细

蓝桥杯省赛[新手向题解] 2019 第十届 C/C++ A组 第九题 DP

时间:2019-03-28 00:03:24      阅读:295      评论:0      收藏:0      [点我收藏+]

蓝桥杯省赛[新手向题解] 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<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

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!