2021-09-07 停课集训R8解题报告

停课集训R8解题报告

小萌新到第5天才有空写题解,心都碎了

哼,哼,啊啊啊啊啊啊啊啊啊啊啊啊!

R8:DP初级1

实话说,以前DP是我最讨厌的类型。

今天只A了2道题。可以说是十分痛苦

T1
问题 A: 石子合并(pebble)

题目描述

在一个圆形操场的四周摆放了n堆石子(n< 100),现要将石子有次序地合并成一堆。规定每次只能选相邻的两地合并成新的一堆, 并将新的一堆的石子数记为该次合并的得分。
编程,读入堆数n及每堆石子数(≤20),选择一种合并石子的方案,使得做n-1次合并,得分的总和最小;选择一种合并石子的方案,使得做n-1次合并,得分的总和最大。

输入

第1行为石子堆数,第2行为每堆石子数,每两个数之间用一个空格分隔。

输出

第1行为最小得分值,第2行为最大得分值。

样例输入

4
4 5 9 4

样例输出

43
54

区间DP水题, 一开始数组开小了,后来顶着开就过了。

告诉我们一个道理,不管范围多大上来就顶着开完事了

for(int lth = 2 ; lth <= n ; ++lth)
    {
        for(int i = 1 ; i <= (2 * n) - lth + 1 ; ++i)
        {
            j = i + lth - 1;
            for(int k = i ; k < j ; ++k)
            {
                f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j] 
                              + enquire(k + 1, j) + enquire(i, k));
                d[i][j] = min(d[i][j], d[i][k] + d[k + 1][j] 
                              + enquire(k + 1, j) + enquire(i, k));
            }
        }
    }

定义F(I)(J) 表示 i - j 的最大值,d为最小值

之后就是区间暴D。

T2
问题 B: 对抗赛(compete)

题目描述

程序设计对抗赛设有 N(0<N≤50 的整数)个价值互不相同的奖品,每个奖品的价值分别为 S1,S2,S3……Sn(均为不超过 100 的正整数)。现将它们分给甲乙两队,为了使得甲乙两队得到相同价值的奖品,必须将这 N 个奖品分成总价值相等的两组。
编程要求:对给定 N 及 N 个奖品的价值,求出将这 N 个奖品分成价值相等的两组,共有多少种分法?

输入

第一行一个整数N;
第二行N个数据:S1,S2,S3……Sn,每两个相邻的数据之间有一个空格隔开。

输出

一行一个整数,表示多少种分法的答案,数据若无解,则输出 0。

样例输入

7
1 2 3 4 5 6 7

样例输出

4

可行性背包版,一开始数组开的size错了,wa

告诉我们一个道理,不管范围多大上来就顶着开完事了

if(m&1) return printf("0") & 0;
        m >>= 1;
        dp[0] = 1;
        for(int i = 1 ; i <= n ; ++i)
        {
            for(int j = m ; j >= val[i]; --j)
            {
                dp[j] += dp[j - val[i]];
            }
        }

首先,m(sum)不能整除2就直接不行

然后就是一个版

显然,dp(j)表示值为j的方案数的话,

显然可以通过“选择第i个物品"这个意义从dp(j - val[i])转移过来。

T3
问题 C: 演讲大厅安排(hall)

题目描述

有一个演讲大厅需要我们管理,演讲者们事先定好了需要演讲的起始时间和中止时间。我们想让演讲大厅得到最大可能的使用。我们要接受一些预定而拒绝其他的预定,目标是使演讲者使用大厅的时间最长。假设在某一时刻一个演讲结束,另一个演讲就可以立即开始。
编程任务:
1、输入入演讲者的申请。
2、计算演讲大厅最大可能的使用时间。
3、输出结果。

输入

第一行为一个整数 N,N≤5000,表示申请的数目。
以下 n 行每行包含两个整数 p,k,1 ≤ p < k ≤ 30000,表示这个申请的起始时间和中止时间。

输出

一个整数,表示大厅最大可能的使用时间。

样例输入

12
1 2
3 5
0 4
6 8
7 13
4 6
9 10
9 12
11 14
15 19
14 16
18 20

样例输出

16

唯二A的题之一, emm

直接上状态了

定义 f(i)(0) 表示从1到第i场强制不选第i场的最大值,

​ f(i)(1)表示从1到第i场强制选第i场的最大值,

则 f(i)(0) = max(f(i-1)(0), f(i-1)(1));

​ f(i)(1)…

直接上代码

for(int j = i - 1; j > 0 ; --j)
        {
            if(a[j].r <= a[i].l)
            k = max(k, f[j][1]);//选择结束时间点早于第i场的演讲中,值最大的那个的值
        }
        f[i][1] = k + a[i].time();
//time(){if(ti) return ti;return ti = (r - l);}
T4
问题 D: 传纸条(message)

题目描述

小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个 m 行 n 列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用 0 表示),可以用一个 0-100 的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度只和最大。现在,请你帮助小渊和小轩找到这样的两条路径。

输入

第一行有 2 个用空格隔开的整数 m 和 n,表示班里有 m 行 n 列(1<=m,n<=50)。
接下来的 m 行是一个 m*n 的矩阵,矩阵中第 i 行 j 列的整数表示坐在第 i 行 j 列的学生的好心程度。每行的 n 个整数之间用空格隔开。

输出

共一行,包含一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。

样例输入

3 3
0 3 9
2 8 5
5 7 0

样例输出

34

经典的二路DP好吧,看数据范围:

可以定义 f(a)(b)©(d) 表示A在(

a,b),B在(c,d)时,可以得到的最大价值

现在还有两个问题:

1.路径重叠

2.路径同步

第二个很容易解决,直接让B和A一起从(1,1)出发

而第一个问题,可以通过在f上减去重叠的值排除干扰

接下来直接暴D完事:

for(int a = 1 ; a <= n ; ++a)
        for(int b = 1 ; b <= m ; ++b)
            for(int c = 1 ; c <= n ; ++c)
                for(int d = 1 ; d <= m ; ++d)
                {
                    f[a][b][c][d] = Max(//四种转移意义:
                                        f[a-1][b][c-1][d],//A↓ B↓
                                        f[a-1][b][c][d-1],//   B→
                                        f[a][b-1][c-1][d],//A→ B→
                                        f[a][b-1][c][d-1]//    B↓
                                    ) + mb[a][b] + mb[c][d];

                    if(a == c && b == d)
                        f[a][b][c][d] -= mb[c][d];
                }
T5
问题 E: 守望者的逃离(Noip2007)

恶魔猎手尤迪安野心勃,他背叛了暗夜精灵,率领深藏在海底的娜迦族企图叛变。守望者在与尤迪安的交锋中遭遇了围杀,被困在一个荒芜的大岛上。为了杀死守望者,尤迪安开始对这个荒岛施咒,这座岛很快就会沉下去。到那时,岛上的所有人都会遇难。守望者的跑步速度为 17m/s,以这样的速度是无法逃离荒岛的。庆幸的是守望者拥有闪烁法术,可在1s 内移动 60m,不过每次使用闪烁法术都会消耗魔法值 10 点。守望者的魔法值恢复的速度为 4 点/s,只有处在原地休息状态时才能恢复。
现在已知守望者的魔法初值 M,他所在的初始位置与岛的出口之间的距离 S,岛沉没的时间 T。你的任务是写一个程序帮助守望者计算如何在最短的时间内逃离荒岛,若不能逃出,则输出守望者在剩下的时间内能走的最远距离。注意:守望者跑步、闪烁或休息活动均以秒(s)为单位,且每次活动的持续时间为整数秒。距离的单位为米(m)。

输入

仅一行,包括空格隔开的三个非负整数 M, S, T。

输出

包含两行:
第1 行为字符串“Yes”或“No”(区分大小写),即守望者是否能逃离荒岛。
第2 行包含一个整数。第一行为“Yes”(区分大小写)时表示守望者逃离荒岛的最短时间;第一行为“No”(区分大小写)时表示守望者能走的最远距离。

样例输入

39 200 4

样例输出

No
197

只有三种方法,一直闪加休息,一直跑,或者跑加闪

实际上,显然任何时候闪与跑对距离的贡献是一样的

于是显然,可以将闪加跑等价成先闪再跑

于是:

for(int i = 1 ; i <= T ; ++i)
    {
        if(M >= 10) d0 += 60,M -= 10;
        else M += 4;
        d1 += 17;
        d1 = max(d0, d1);
        if(d1 >= S) return printf("Yes\n%d\n", i) & 0;
    }
    printf("No\n%d\n",d1);
T6:
T7:
问题 G: 城市交通(traffic)

题目描述

有n个城市,编号1~n,有些城市之间有路相连,有些则没有,有路则会有一个距离。图所示为一个含有11个城市的交通图,连线上的数(权)表示距离。现在规定只能从编号小的城市到编号大的城市。问:从编号为1的城市到编号为n的城市之间的最短距离是多少?

img

输入

第1行为n,表示城市数,n≤100。
下面的n行是一个nxn的邻接矩阵map[i,j],其中map[i,j]=0表示城市i和城市j之间没有路相连,否则为两者之间的距离。

输出

一行一个数,表示最短距离。数据保证-定可以从城市1到城市n。

样例输入

11
05300000000
50016300000
30008040000
01000005600
06800005000
03000000080
00400000030
00055000003
00060000004
00000830003
00000003430

样例输出

13

水题,定义Dp(i)表示第i个点到终点的距离

倒推

for(int i = n ; i >= 1 ; --i)
for(int j = 1 ; j < i ; ++j)
f[j] = min(f[j], f[i] + a[i][j]);
T8
问题 H: 最长公共子序列(lcs)

题目描述

给定两个字符串序列X、Y,长度不超过5000,求出两个序列的最长公共子序列长度。注意:子序列不是子串,不要求连续,例如两个字符串enblogs和belong的公共子序列为blog。可以发现,最长公共子序列是不唯一的,但是长度一定是唯一的。

输入

两行,每行一个字符串。

输出

一行一个整数,表示答案。

样例输入

cnblogs
belong

样例输出

4

定义DP(i)(j)表示A的i前缀与B的j前缀匹配的最长长度

此时再有转移方程:

当 a[i] == b[j] 时 dp[i][j] = dp[i-1][j-1] + 1
当 a[i]= b[j] 时 dp[i][j] = max(dp[i-1][j], dp[i][j-1])

所以,有代码曰:

等等,一开始MLE了,回来发现要滚动数组(看代码有注释)

for(int i = 0 ; i < lx ; ++i)
    {
        for(int j = 0 ; j < ly ; ++j)
        {
            if(x[i] == y[j])
                f[p][j] = f[p ^ 1][j - 1] + 1;//q^1表示“另一维”(成对变换01)
            else if(x[i] != y[j])
                f[p][j] = max(f[p ^ 1][j], f[p][j-1]);
        }
        p ^= 1;//切换另一维
    }
T9:
T10:
问题 J: 最长公共上升子序列时间

题目描述

熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。

小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。

小沐沐说,对于两个数列A和B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。

奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。

不过,只要告诉奶牛它的长度就可以了。

数列A和B的长度均不超过3000。

输入

第一行包含一个整数N,表示数列A,B的长度。

第二行包含N个整数,表示数列A。

第三行包含N个整数,表示数列B。

输出

输出一个整数,表示最长公共子序列的长度。

样例输入

4
2 2 1 3
2 1 2 3

样例输出

2

和上一题一样

for (int i = 1; i <= n; i++)
    {
        int val = 0;
        for (int j = 1; j <= n; j++)
        {
            f[i][j] = (a[i] == b[j] ? val + 1 : f[i-1][j]);
            if (b[j] < a[i])
                val = max(val, f[i][j]);
        }
    }

值得提出的是,由于决策集合不会减小,直接记录最优答案即可

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值