zoukankan      html  css  js  c++  java
  • dp 做题记录

    持续更新中~(尽量不咕)

    P1650 田忌赛马

    有很明显的贪心思路,用田忌最烂的马去怼齐王最好的马。

    由于田忌是有主动权的,所以我们直接让齐王从优到劣出马。

    设计 \(dp\) 状态:

    1. 普通 \(dp\)\(dp_{i, j}\) 表示齐王的前 \(i\) 匹马,对上了田忌的前 \(j\) 匹马和后 \(i - j\) 匹马(都按从大到小排序)的最大盈利。

      转移方程:

      \[dp_{i, j} = max\{f_{i - 1, j} + g_{n - (i - j) + 1, i}, f_{i - 1, j - 1} + g_{j, i}\} \]

      \(g_{i, j}\) 表示田忌的第 \(i\) 匹马和齐王的第 \(j\) 匹马怼上之后赢/输的钱数。

    2. 区间 \(dp\)\(dp_{i, j}\) 表示田忌使用 \(i \sim j\) 的马的时候最大盈利,对手是齐王最拉的 \(len\) 匹马,所以转移的时候跟 \(b_{n - len + 1}\) 比即可。


    P1868 饥饿的奶牛

    贪心显然是可以做的。

    emm……但是 \(dp\) 状态一直没想出来,看了一眼题解,发现直接 \(f_i\) 表示前 \(i\) 格最大获利,转移的时候枚举所有左端点在 \(i\) 前面的区间,取 \(max\) 即可。


    P1725 琪露诺

    明显的单调队列优化 \(dp\),状态也比较好想,\(f_i\) 表示前 \(i\) 格最大获利,边界稍微判一判即可。


    P1417 烹调方案

    看着就很背包,然而发现纯背包不是很可做,因为跟 \(t\) 有关。

    我们要想办法把 \(t\) 的影响消掉,对于两个物品 \(x\)\(y\),把 \(x\) 在前,\(y\) 在后的获利和 \(y\) 在前,\(x\) 在后的获利列出来。

    发现前面优于后面的条件是 \(x_c * y_b < y_c * x_b\)

    按这个排序,然后跑个 01背包即可。


    AT2000 [AGC002F] Leftmost Ball

    非常妙的一道题。

    首先转换以下题意,放 \(n\) 个白球和 \(n \times (k - 1)\) 个其他颜色的球有多少种合法方案。

    不难发现,任意前缀中白球个数一定大于其它颜色的球的个数。

    所以我们设 \(dp_{i, j}\) 表示放了 \(i\) 个白球和 \(j\) 种其他颜色的球(全部放了)的方案数。

    转移的时候可以从 \(dp_{i - 1, j}\)\(dp_{i, j - 1}\) 转移过来。

    • \(dp_{i - 1, j}\) 转移,就是再多放一个白球,显然一定是合法的。
    • \(dp_{i, j - 1}\) 转移,从剩下 \(n - j + 1\) 种没有放置的颜色中选一种放置,然后该种颜色的球还有 \(k - 2\) 个没有放,此时我们还剩 \(n \times k - (j - 1) \times (k - 1) - 1\) 个位置,所以再乘上 \(\dbinom{n \times k - (j - 1) \times (k - 1) - 1}{k - 2}\) 即可。

    总结一下:

    \[dp_{i, j} = dp_{i - 1, j} + dp_{i, j - 1} \times (n - j + 1) \times \dbinom{n \times k - (j - 1) \times (k - 1) - 1}{k - 2} \]


    P1433 吃奶酪

    link

    似乎是最基础的一道状压 \(dp\) 题目了,报搜似乎也可以过,当然这里只谈 \(dp\)

    状压 \(dp\) 的转移状态一般都比较套路,都是设 \(f_{s, i}\) 表示选取了状态为 \(s\) 的物品,最后一个选的是 \(i\) 时的答案,那么这道题也是一样的。

    先提一嘴,忘了是哪个学长讲的了,开多维数组时,小的放前,大的放后常数要小很多。应用场景:倍增,状压。

    我们设 \(f_{i, s}\) 表示已经吃掉了状态为 \(s\) 的奶酪,最后一个吃的是 \(i\) 时的最小距离。

    转移也比较简单:

    \[f_{i, s} = min(f_{i, s}, f_{j, s \oplus (1 << (j - 1))} + dist(p_i, p_j)) \]

    \(\oplus 表示异或。这个方程比较显然吧。。就不解释了。\)

    (我不会告诉你我一开始输入坐标用的快读,得到了 80pts 的好成绩,下了个数据发现坐标也是浮点数。。)


    P1284 三角形牧场

    link

    一开始想的是就记一个 \(f_i\) 表示长度为 \(i\) 的边能否凑出来,然后去枚举三角形,但是这样没办法判断一条边是否被使用了两次及以上,所以考虑多记一点东西。

    \(f_{k, i, j}\) 表示当前已经考虑到第 \(k\) 块木板,能否被表示出长度为 \(i\)\(j\) 的边,第三条边直接 \(sum - i - j\) 即可。

    发现这样的状态似乎还可以,接着考虑转移方程。

    假设当前第 \(k\) 块木板的长度是 \(a_k\),我们分类讨论一下:

    • 放到 \(i\) 边上:\(f_{k, i, j} |= f_{k - 1,i - a_k, j}\)
    • 放到 \(j\) 边上:\(f_{k, i, j} != f_{k - 1,i, j - a_k}\)
    • 放到第三条边上:就是自己等于自己

    总结一下:

    \[f_{k, i, j} \ |= f_{k - 1, i - a_k, j} \ |\ f_{k - 1, i, j - a_k} \]

    不难发现,\(f_{k, i, j}\) 的转移只与 \(f_{k - 1}\) 有关,所以滚动一下即可。

    这样一来,我们就可以愉快的进行转移啦。

    \(dp\) 完之后,枚举所有的 \(f_{i, j}\),判一下能否构成三角形,并用海伦公式求一下面积即可。


    USACO 2021 December Contest, Bronze [Problem 3. Walking Home]

    被 T1 打爆了,看错题调了 2.5h,直接心态爆炸。

    好吧我们还是来看这道题。

    非常明显的 \(dp\),状态也很简单。设 \(dp_{i, j, d, k}\) 表示从方向 \(d\) 走到 \((i, j)\),转了 \(k\) 次方向的方案数。

    方程也比较好想,就贴代码吧。

    // d = 0 下,1 右
    dp[1][1][0][0] = 1, dp[1][1][1][0] = 1;
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j){
            if(s[i][j] == 'H') continue;
            for(int k = 0; k <= m; ++k){
                dp[i][j][0][k] += dp[i - 1][j][0][k];
                dp[i][j][1][k] += dp[i][j - 1][1][k];
                if(k > 0){
                    dp[i][j][0][k] += dp[i][j - 1][1][k - 1];
                    dp[i][j][1][k] += dp[i - 1][j][0][k - 1];
                }
            }
        }
    

    考场上被 T1 弄得没心态了,转移的时候还枚举了个 \(d\),导致重复转移,以为 \(dp\) 不可写,又改成了记忆化搜索,结果也没改出来……


    AT3857 [AGC020C] Median Sum

    link

    fls 的每日一题。

    做这道题正好学习了一下用 \(bitset\) 做完全背包(只记录是否可达)。

    我们设 \(f_{i, j}\) 表示用前 \(i\) 个数字能否表示出 \(j\)

    转移方程:

    \[f_{i, j} = f_{i - 1, j - a_i}\ \mid\ f_{i - 1, j} \]

    可以把第一维删掉,然后就是一个完全背包了。

    再来看如何计算答案。

    我们考虑暴力枚举中位数是多少,从 \(\frac {sum}2\) 枚举到 \(sum\),若 \(f_x\) 存在,那么 \(f_{sum - x}\) 必然存在。

    所以找到的第一个 \(f_{x} = 1\)\(x\) 就是答案。

    顺便贴一下 \(bitset\) 实现完全背包的代码。

        f[0] = 1;
        for(int i = 1; i <= n; ++i){
            int x = read();
            f |= (f << x), sum += x;
        }
    

    P1541 [NOIP2010 提高组] 乌龟棋

    link

    又双叒叕定义错状态了。

    很显然的状态是 \(dp[k][a][b][c][d]\),但是明显开不下,所以考虑去掉不需要的状态。

    一开始想的是只记录三种卡片的个数,也就是 \(dp[k][a][b][c]\),然后第一维再滚动一下,就差不多了,但实际上四种卡片并没有本质联系,简单来说就是没办法通过其它三种卡片的个数来推出第四种卡片的个数。

    所以要换一维压掉。

    这时我们的目光移到了第一维上,因为删掉它其他的就不用管了。

    那么事实上我们确实可以删掉它。

    假设已经枚举出了四种卡片的个数 \(a,b,c,d\),我们可以直接通过 \(k = 1 + a + 2b + 3c + 4d\) 来计算出当前到了哪里。

    题目中也说了所有卡片的和就是 \(n\),所以输出答案时直接输出即可。

    总结一下:

    \(dp\) 状态:\(dp[a][b][c][d]\)

    转移方程:(比较麻烦,贴代码吧)

        dp[0][0][0][0] = v[1];
        for(int a = 0; a <= t[1]; ++a)
            for(int b = 0; b <= t[2]; ++b)
                for(int c = 0; c <= t[3]; ++c)
                    for(int d = 0; d <= t[4]; ++d){
                        int k = 1 + a + 2 * b + 3 * c + 4 * d;
                        if(a) dp[a][b][c][d] = max(dp[a][b][c][d], dp[a - 1][b][c][d] + v[k]);
                        if(b) dp[a][b][c][d] = max(dp[a][b][c][d], dp[a][b - 1][c][d] + v[k]);
                        if(c) dp[a][b][c][d] = max(dp[a][b][c][d], dp[a][b][c - 1][d] + v[k]);
                        if(d) dp[a][b][c][d] = max(dp[a][b][c][d], dp[a][b][c][d - 1] + v[k]);
                    }
    

    P1280 尼克的任务

    link

    状态很巧妙的 \(dp\)

    观察到数据范围的值域都是 \(10^4\),想的 \(dp_{i}\) 表示 \(1 \sim i\) 最大的休息时间,然而并不知道如何转移。

    然后觉得可能有单调性,就是从前面哪些任务转移过来是单调的,但是想了想发现并没有单调性。

    所以换一种设计思路,设 \(dp_i\) 表示从 \(i \sim n\) 做多能休息多长时间。

    转移分两种情况:

    • \(time_i\) 上并没有工作,那么 \(dp_i = dp_{i + 1} + 1\)
    • \(time_i\) 上有工作,枚举每一个工作 \(j\)\(dp_i = max\{dp_{i + t_j}\}\)

    然后就没了,可以开个 \(vector\) 记录一下开始时间在 \(i\) 的工作有哪些,方便枚举。


    P1613 跑路

    link

    其实这道题也不能算是完全意义上的 \(dp\) 吧,如果思维局限在了 \(dp\) 里就做不出来了QwQ。

    下面简单讲一下这题的解法。

    看到 \(2^k\),很自然的想到倍增,但是倍增怎么用呢?

    我们设 bool 类型的数组 \(g_{i, j, t}\) 表示是否存在 \(i \sim j\) 长度为 \(2^t\) 的路径,转移的过程类似于 \(Floyd\)(数据范围这么小不写 \(Floyd\) 是不是傻 )。

    然后对于 \(g_{i, j, t} = 1\)\((i, j)\),令 \(dis_{i, j}\) 也等于 1 (\(dis\) 数组初值都是无穷大),然后对 \(dis\) 再跑一遍 \(Floyd\) 即可。

    附预处理的代码:

    \(Code:\)

    inline void prework(){
        for(int t = 1; t < N; ++t)
            for(int k = 1; k <= n; ++k)
                for(int i = 1; i <= n; ++i)
                    for(int j = 1; j <= n; ++j)
                        if(g[i][k][t - 1] && g[k][j][t - 1])
                            g[i][j][t] = 1, dis[i][j] = 1;
    }
    
    

    P1622 释放囚犯

    link

    本来想了想区间 \(dp\),结果觉得不可做,后来看了一眼题解发现真的是区间 \(dp\)(震惊)!

    然后又想了想发现确实是可以转移的。

    \(dp_{i, j}\) 表示释放 \(i \sim j\) 的囚犯的最小花费(这里的 \(i \sim j\) 指输入的需要被释放的囚犯的编号)

    转移的时候枚举断电 \(k\),方程:

    \[dp_{i, j} = min\{dp_{i, k - 1} + dp_{k + 1, j} + (a_{j + 1} - a_{i - 1} - 1) - 1 \} \]

    还是非常显然的,最后的 -1 是要减去自己。

    我们一开始想区间 \(dp\) 的时候觉得 \(a_{j + 1} - a_{i - 1} - 1\) 这部分没法计算,其实你 \(dp_{i, k - 1}\)\(dp_{k + 1, j}\),就已经固定了范围了。

    另外一种方法:非常巧妙!把删去点换成合并,把每两个要被删去囚犯之间的囚犯个数当作一个新点的权值,然后就按照合并石子的思路跑一边区间 \(dp\) 即可。


    P1854 花店橱窗布置

    link

    观察到数据范围 \(\leq 100\),状态还是非常好想的。

    \(dp_{i, j}\) 表示在前 \(j\) 个花盆中放了 \(i\) 朵花的最大收益。

    转移的时候可以从 \(dp_{i - 1, k}\ (k < j)\) 转移过来,也就是把前 \(i - 1\) 盆花放好,第 \(i\) 盆花放到 \(j\) 的最大收益。

    转移方程:

    \[dp_{i, j} = max\{dp_{i - 1, k} + a_{i, j}\} \]

    由于这道题还需要输出方案,所以转移的时候记录一下从哪里转移过来的,最后递归输出即可。


    AT2171 [AGC007D] Shik and Game

    link

    题意就不解释了。。

    考虑一下 Shik 走路的全过程——不停地来回走。

    有的路段只经过了一次,有的经过了三次。

    那么我们设 \(f_i\) 表示拿到了前 \(i\) 个金币所花费的最小时间,由于是最小时间,那么他必定在 \(a_i\) 这个位置上。

    转移方程:

    \[f_i = min\{f_j + (a_i - a_j) + max(2(a_i - a_{j + 1}), T)\} \]

    这是什么意思呢?

    • 第一个 \(a_i - a_j\) 表示的是从 \(a_j\) 走到 \(a_i\) 花费的时间。

    • \(2(a_i - a_{j + 1})\) 指折返回去再走回来,所以要乘 2。

    • \(T\) 是因为可能折回去之后还没有金币,所以还得等到 \(T\),所以要取 \(\max\)

    不难发现,\(a_i - a_j\) 的和就是 \(a_n\),所以可以不用管,最后输出答案的时候加上 \(E\) 即可。

    再来看如何转移,要分类讨论一下:

    • \(2(a_i - a_{j + 1}) > T\) 时,\(f_i = f_j + T\).
    • \(2(a_i - a_{j + 1}) \leq T\) 时,\(f_i = f_j + 2(a_i - a_{j + 1})\).

    容易发现,对于固定的 \(i\)\(a_i - a_{j + 1}\)\(j = 1 \sim i - 1\) 时是单调递减的,所以直接单调队列跑一遍即可,或者拿个指针扫一遍也行。


    P1353 [USACO08JAN]Running S

    link

    很水的一道 \(dp\)

    观察到两种操作消耗的时间都是 1 分钟,所以我们直接设 \(dp_i\) 表示在第 \(i\) 分钟时疲劳度为 0 所能跑的最远距离。

    我们要让它疲劳度为 0 必须在后面留下足够多的休息时间,所以要倒着枚举。

    转移的时候我们再枚举一个 \(j\),表示从 \(i\) 开始往后的 \(j\) 分钟用来跑步,由于疲劳度得是 0,所以还得给它分配 \(j\) 分钟的休息时间,从 \(dp_{i + 2 \times j}\) 转移过来。

    转移方程:

    • \(dp_i = dp_{i + 1}\),第 \(i\) 分钟休息。
    • \(dp_i = dp_{i + 2 \times j} + (sum_{i + j - 1} - sun_{i - 1})\),后面的这个减法就是 \(i \sim i + j - 1\) 这段时间用来跑步能跑的距离。

    这个前缀和相减的式子中的下角标自己随便手玩一下就好。


    CF580D Kefa and Dishes

    link

    好水的状压 \(dp\)……

    非常套路的状态,设 \(dp_{i, s}\) 表示吃了状态为 \(s\) 的菜,且最后一个吃的是 \(i\) 的最大满意度。

    那么如何转移呢?

    枚举状态 \(s\),枚举最后一个吃的 \(i\),再枚举倒数第二个吃的菜 \(j\ (i, j \in s)\)

    转移方程:

    \[dp_{i, s} = \max\{dp_{j, s \oplus (1 << (i - 1))} + a_i + g_{j, i} \} \]

    \(\oplus\):表示异或。

    \(a_i\):表示单独吃 \(i\) 的满意度。

    \(g_{i, j}\):表示先吃 \(i\) 然后紧跟着吃 \(j\) 的额外满意度。

    这个转移方程的意思是:从吃了状态为 \(s \oplus (1 << (i - 1))\) 的菜(就是不吃 \(i\)),且最后一个吃的是 \(j\) 转移到 \(dp_{i, s}\),要加上 \(i\) 的满意度,再加上吃了 \(j\) 之后紧跟着吃 \(i\) 的额外满意度。

    还是非常显然的吧。

    注意:额外满意度没有双向性。

    统计答案的时候枚举一下状态 \(s\),再枚举最后吃的 \(i\),如果 \(s\) 中有 \(m\) 个 1,就取 \(\max\)


    P1356 数列的整除性

    link

    观察到一共只有 \(10^4\) 个数,且 \(k \leq 100\),暴力 \(dp\) 即可。

    \(dp_{i, j}\) 表示计算到了第 \(i\) 个数能否凑出和为 \(j\)

    转移方程:

    \[dp_{i, j} = dp_{i - 1, calc(j - a_i)} \mid dp_{i - 1, calc(j + a_i)} \]

    \(clac()\) 指模 \(k\) 之后的结果。

    初值 \(dp_{1, calc(a_1)} = dp_{1, calc(-a_1)} = 1\)


    CF95E Lucky Country

    link

    二进制拆分优化背包

    非常巧妙的 \(dp\) 题,sto xy学长 orz

    同一个区域的岛屿明显只能在一起,所以用并查集合并一下同一区域内的岛屿。

    然后我们要找出用最少的边连出一个幸运地区。

    那这不就是一个背包吗!

    朴素的状态:\(dp_{i, j}\) 表示考虑到前 \(i\) 个区域,一共有 \(j\) 个岛屿的最小连边数。

    朴素的转移方程:

    \[dp_{i, j} = \min\{dp_{i - 1, j - a_i}\} \]

    \(a_i\):表示区域 \(i\) 中的岛屿个数。

    显然会 T,考虑优化。

    经典的二进制拆分就登场了,所以我们二进制拆一下跑个 01背包 即可。


    P2734 [USACO3.3]游戏 A Game

    link

    非常好的区间 \(dp\)

    区间 \(dp\) 应该比较显然吧,但是我一开始写的时候对于如何转移表示疑惑。

    一人拿一次怎么判断?难道再多开一维数组标记吗?

    后来发现其实并不用。

    我们设:\(dp_{i, j}\) 表示一个人选取区间 \(i \sim j\) 的最大收益(可能是先手也可能是后手)。

    假设我们当前在考虑 \(dp_{i, j}\),那么有可能从 \(dp_{i + 1, j}\)\(dp_{i, j - 1}\) 转移过来。

    \[dp_{i, j} = \max\{ (sum_j - sum_i) - dp_{i + 1, j} + a_i\} \]

    这时 \(dp_{i + 1, j}\) 就表示另一个人选择 \(i + 1 \sim j\) 的最大收益,用 \(sum_{i + 1 \sim j}\) 减去 \(dp_{i + 1, j}\) 再加上 \(a_i\) 就是当前这个人选择左边的收益。

    观察到 \(sum_j - sum_i + a_i\) 可以合并一下。

    那么最终的转移方程就是:

    \[dp_{i, j} = (sum_j - sum_{i - 1}) - \min\{dp_{i + 1, j}, dp_{i, j - 1} \} \]


    P4095 [HEOI2013]Eden 的新背包问题

    link

    二进制拆分优化多重背包

    不要忘记了背包原本是有两维状态的!

    我们就正着跑一遍背包,再倒着跑一遍背包。

    对于每组询问求 \(\max\{f_{d - 1, v} + g_{d +1, e - v}\}\)

    \(f\) 是正着的背包,\(g\) 是倒着的背包,这个也比较显然吧。。不解释了。

    但是直接跑多重背包会 \(T\),所以二进制拆分优化一下转换成 01背包 即可通过此题。


    P1410 子序列

    link

    非常非常离谱的状态(正好可以拓宽一下思维)。

    \(dp_{i, j}\) 表示前 \(i\) 个数中,一个上升子序列的长度为 \(j\) 且以 \(i\) 结尾(以下称 A 序列),存的值是另一个上升子序列(以下称 B 序列)最后一位的大小。

    转移要讨论一下:

    • \(a_i < a_{i + 1}\):这时我们可以直接把 \(a_{i + 1}\) 接到 A 序列上,转移方程(因为 \(a_{i + 1}\) 接到了 A 的后面,B 没有变,所以与 \(dp_{i, j}\) 取最小值):

      \[dp_{i + 1, j + 1} = \min\{dp_{i + 1, j + 1}, dp_{i, j}\} \]

    • \(dp_{i, j} < a_{i + 1}\):这时我们可以把 \(a_{i + 1}\) 接到另一个上升子序列上,那么转移方程就是:

      \[dp_{i + 1, i - j + 1} = \min\{dp_{i + 1, i - j + 1}, a_i \} \]

      \(a_{i + 1}\) 接到了 B 序列上面,用 \(dp\) 数组表示新的 B 序列就是 \(dp_{i + 1, i - j + 1}\),根据 \(dp\) 数组的定义 \(a_i\) 是属于 A 序列的,所以取 \(\min\{a_i\}\)


    P2359 三素数数

    link

    说明/提示 里的区域动归是几个意思啊 QwQ

    考虑 \(dp\) 状态怎么设计,观察到题目只要求 3 位数字,所以直接把最后两位表示出来即可,所以 \(dp_{i, j}\) 表示前 \(i\) 位最后两位是 \(j\) 的合法的数的个数。

    转移的时候我们枚举一个质数 \(p\) (三位数),然后

    \[dp_{i, p \% 100} += dp_{i - 1, p / 10} \]

    然后暴力转移就完了,似乎还有矩乘加速的转移,那真是太强了 orz


    P1156 垃圾陷阱

    link

    非常经典的一道背包题目,把垃圾高度看成重量,能增加的生命长度看成价值。

    背包容量是井的深度,题目就是要求把井填满时的最小价值。

    转移分两种情况,吃 或 填:

    • 吃:\(dp[j] += a[i].l\)
    • 填:\(dp[j + a[i].h] = max(dp[j + a[i].h], dp[j])\)

    随便写写就行了。


    P1537 弹珠

    link

    明显的多重背包 \(dp\),但是直接跑会 T 飞。

    所以随便整个二进制拆分优化多重背包,然后随便跑个 01 背包即可通过此题。


    P1717 钓鱼

    link

    观察到数据范围 \(1 \leq h \leq 16, 1 \leq n \leq 25\),这不是随便写都能过?

    一个明显的性质:不会往回走。

    所以可以贪心搞一下,这里就不多说了,还是来看怎么 \(dp\)

    其实直接硬上 \(dp\) 就行,设 \(dp_{i, j}\) 表示在第 \(i\) 个池塘,一共花了 \(j\) 分钟的最大收益。

    转移的时候枚举一下在当前池塘钓了 \(k\) 分钟。

    然后:

    \[dp_{i, j} = \max\{ dp_{i - 1, j - t[i - 1] - k} + k \times f_i - k \times (k - 1) / 2 \times d_i \} \]

    每次转移的时候统计一下最大值当答案即可。


    P1021 [NOIP1999 提高组] 邮票面值设计

    感觉这题挺困难的 QwQ,也可能是我太菜了。

    标签里只有 dfs,所以考虑怎么 dfs。

    dfs 的时候记录一下当前搜到了 \(cnt\) 种面值,最大能表示到 \(maxs\)

    然后我们枚举下一张邮票的面值,再用背包算一下能连续表示到多大,把计算出来的值传回去继续 dfs 即可。


    P2736 [USACO3.4]“破锣摇滚”乐队 Raucous Rockers

    link

    依次考虑每一首歌,那么分三种情况:

    • 不选。
    • 放到前面的 CD 中。
    • 放到当前 CD 中。

    \(dp_{i, j}\) 表示用了 \(i\) 张 CD 且最后一张用了 \(j\) 分钟能存下最多歌曲数目是多少。

    转移方程依次对应上面三种情况就是:

    • \(dp_{i, j}\)
    • \(dp_{i - 1. t} + 1\)
    • \(dp_{i, j - a[i]} + 1\)

    \(t\) 表示每张 CD 能容纳的时间,\(a_i\) 表示第 \(i\) 首歌的长度。

    贴一下转移的代码吧。

    for(int i = 1; i <= n; ++i)
        for(int j = m; j >= 1; --j)
            for(int k = t; k >= a[i]; --k)
                dp[j][k] = max(dp[j][k], max(dp[j - 1][t] + 1, dp[j][k - a[i]] + 1));
    

    \[\_EOF\_ \]

  • 相关阅读:
    Android 打开相册拍照选择多张图片显示
    Mac 打开、编辑 .bash_profile 文件
    Ionic app IOS 在Xcode 模拟运行 真机调试
    android studio 把 ionic 打包时修改应用名称、修改应用图标、修改启动画面,升级打包
    Android studio 运行打包 Ionic 项目
    ionic4 路由跳转、ionic4 路由跳转传值 NavController 返回上一页 、NavController 回到根
    Ionic4.x ion-refresher 下拉更新
    Ionic4.x ion-infinite-scroll 上拉分页加载更多
    Ionic4.x ion-infinite-scroll 上拉分页加载更多
    Ionic4.x Modal模态对话框以及 Modal 传值
  • 原文地址:https://www.cnblogs.com/xixike/p/15680710.html
Copyright © 2011-2022 走看看