动态规划的题目。
个人认为学习动态规划更容易理解的学习路径为暴力搜索\(\Rightarrow\)记忆化搜索优化\(\Rightarrow\)动态规划。
Problem A
题目描述
给定\(1×N\)的单行矩阵,矩阵每个元素都是\(-127\)到\(127\)之间的整数。请找到一个连续子矩阵,使得其元素之和最大。
输入数据
多组测试数据,每组数据的第一行为一个整数\(N(N \leq 100)\),第二行包含\(N\)个整数,为行矩阵的\(N\)个元素,每个元素介于\(-127\)到\(127\)之间。
输出数据
最大子矩阵之和,每组对应一行。
样例输入
1 | 10 |
样例输出
1 | 20 |
题目分析
求最大连续子序列之和的题目。
题解
1 |
|
Problem B
题目描述
给定\(N×N\)矩阵,矩阵元素都是\(-127\)到\(127\)之间的整数。请找到一个子矩阵,使得其元素之和最大。
输入数据
多组测试数据,每组测试数据的第一行整数\(N (N \leq 100)\);接下来N行元素,每行N个元素,每个元素介于\(-127\)到\(127\)之间。
输出数据
最大子矩阵元素之和,每组测试数据对应一行。
样例输入
1 | 4 |
样例输出
1 | 15 |
题目分析
思想与A题类似,本题对输入的矩阵进行列的求和,然后用\(i\)和\(j\)对行进行遍历\((1 \leq i \leq j \leq N)\),然后对于确定的\(i\)和\(j\),使用\(k\)进行列的遍历,最后求出最大的结果即可。
题解
1 |
|
Problem C
题目描述
作为一个有很多游戏想买但囊中羞涩的大学生,小明决定在这个暑假开始打工赚钱。经过一段时间的寻找,他一共找到了\(n\)个打工的招聘广告,其中第\(i\)个打工的日期从\(l_i\)开始,到\(r_i\)为止,一共付给他\(c_i\)元钱。因为这些打工的时间都相互冲突,所以同一天小明最多参加一个打工,并且一个打工一旦开始,就必须一直工作到结束,不能中途退出。现在小明想要知道,这个暑假他打工最多能得到多少钱?
输入数据
第一行一个整数\(n(1 \leq n \leq 1000000)\),表示招聘广告的数量;接下来一共\(n\)行,每行3个整数\(l_i,r_i,c_i(1 \leq l_i ≤ r_i \leq 1000000,1 \leq c_i \leq 1000000000)\),表示打工的开始时间,结束时间和报酬。
输出数据
一行一个整数\(k\),表示小明最多能够得到的钱数。
样例输入
1 | 3 |
样例输出
1 | 6 |
题目分析
基本思路
这题目的思路可以从LintCode 515题中得到一些启发。如果采用动态规划的话,对于\(n\)项工作而言,每一项工作只有选和不选两种情况,设\(dp[i][0]、dp[i][1]\)为第\(i\)件工作接受或者不接受的条件下所获得的最大酬劳,那么最终的答案为\(max(dp[n][0],dp[n][1])\)。
但是本题对于每项工作的选择是有后效性的,必须要满足时间的约束条件;而要使得获得的薪酬最大,应该尽可能多做一些工作。根据会场安排的问题可以想到应该按照工作的结束时间进行排序。
会场安排问题
假设某社团某一天要组织\(𝑛\)个活动\(𝐸=\begin{Bmatrix}1, 2,⋯,n\end{Bmatrix}\),其中每个活动都要求使用同一礼堂,而且在同一时间内只有一个活动能使用这个礼堂。每个活动\(i\)都有一个要求使用礼堂的起始时间\(s_i\)和结束时间\(f_i\),且有\(s_i < f_i\)。如果选择了活动\(i\),则它在半开时间区间\([s_i,f_i)\)内占用资源。若区间\([s_i,f_i)\)与区间\([s_j,f_j)\)不相交,则称活动\(i\)与活动\(j\)是相容的。现在给定𝑛个活动的开始时间和结束时间,请设计一个活动安排方案,使得安排的相容活动数目最多。
分析题目,要解决这个问题可以使用二进制枚举,但是时间复杂度是\(O(2^n)\),不可以接受,因此换一个思路。使用贪心的策略大致有三个思路:
- 按照占用时间短优先;
- 按照开始时间早优先;
- 按照结束时间早优先。
对于前两种情况,都可以找出反例证明贪心算法不成立:对于按照占用时间短优先的方案,反例为\([0,10),[9,14),[13,17)\),按照占用时间短优先的话应该先安排\([9,14)\),而最优安排显然是选另外两个;对于按照开始时间早优先的方案,反例为\([0,10),[2,5),[6,7)\),显然。那么如何证明按照结束时间早的贪心策略是正确的呢?
将集合\(S\)中的活动按照结束时间递增顺序排列,即记为\(S=\begin{Bmatrix}1,2,⋯,n \end{Bmatrix}\);假设\(A=\begin{Bmatrix}j_1,j_2,...,j_m \end{Bmatrix}\)是\(S\)的一个最优解,其中活动也按照结束时间递增顺序排列。不妨设\(B=\begin{Bmatrix}A_1,A_2,⋯,A_c \end{Bmatrix}\)为该问题的最佳解构成的集合,显然对于任意一个有限的会场安排问题,总是存在最优解。现在需要证明对于贪心算法得出的这样的解\(A\)总满足\(A \in B\)。
设\(k=1\)时选择活动为\(\begin{Bmatrix}1\end{Bmatrix}\),需要证明存在一个最优解包含\(\begin{Bmatrix}1\end{Bmatrix}\),若\(j_1=1\),那么最优解包含了\(\begin{Bmatrix}1\end{Bmatrix}\);否则,用活动\(1\)替换最优解中的\(j_1\),显然\(A'=\begin{Bmatrix} 1,j_2,...,j_m \end{Bmatrix}\)也是一个相容的活动集合(\(1\)是结束时间最早的活动),也是一个最优的活动安排。
当\(k>1\)时,设\(\{ i_1,1_2,...,i_k \}\)是贪心算法前\(k\)步顺序选择的活动,那么存在一个最优解:\(A=\{ i_1=1,1_2,...,i_k \} \bigcup B\)。假设\(S'\)是\(S\)中与\(\{ i_1, i_2,⋯,i_k \}\)相容的活动,即\(S'=\{ j | s[j]≥𝑓[i_k], j \in 𝑆 \}\),那么\(B\)是\(S'\)的一个最优解。再用刚才关于活动\(1\)的替换等价性证明可知\(𝑆'\) 的结束时间最早的活动\(𝑖_{𝑘+1}\)包含在某一个最优解\(B'\)中,则可以构造原问题的一个最优解为\(A'=\{ i_1=1,1_2,...,i_k,i_{k+1} \} \bigcup (B - \{ i_{k+1} \})\)。
最终的代码如下所示。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using namespace std;
struct activity {
int start;
int end;
bool operator<(const activity& b) const{
return end < b.end;
}
};
int main() {
int n;
cin >> n;
if (n < 1) {
return 0;
}
vector<activity> act(n);
for (auto& i : act) {
cin >> i.start >> i.end;
}
sort(act.begin(), act.end());
int ans = 1;//选择第一个活动
int last_selected = 0;
for (int i = 1; i < n; i++) {
if (act[i].start >= act[last_selected].end) {
last_selected = i;
ans++;
}
}
cout << ans << endl;
return 0;
}
/*
测试用例1(答案为2)
3
0 10
2 5
7 9
测试用例2(答案为2)
3
0 4
3 5
4 9
*/
网上查找了资料后发现本题或许可以使用树状数组来解决。
树状数组
树状数组可以解决大部分基于区间上的更新以及求和问题。设结点编号为\(x\),那么该节点维护的值为\((x-\text{lowbit}(x),x]\)的和,其中\(\text{lowbit}(x)=x^k\),其中\(k\)为\(x\)最低位1所在的二进制位。这种情况下要求树状数组的编号范围是\([1,2^n]\)。
\(\text{lowbit}\)值一般使用\(x \& (-x)\)的方式计算,这一点利用了补码的运算性质,证略。
本题目使用树状数组进行了工作结束日期的统计。 最后改完代码后发现用
vector
空间会爆掉,所以直接使用空间足够大的数组即可。
题解
1 |
|
Problem D
题目描述
给定一个信封,最多只允许粘贴\(N\)张邮票,计算在给定\(M(N+M \leq 10)\)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大\(max\),使得\(\{1,\dots ,max\}\)的每一个邮资值都能得到?
输入数据
一行,分别为\(N,M\)。
输出数据
第一行为\(m\)种邮票的面值,按升序排列,各数之间用一个空格隔开;第二行为最大值,格式为:"MAX=%d"
。
样例输入
1 | 3 2 |
样例输出
1 | 1 3 |
题目分析
对于已知邮票面值的情况下,可以使用动态规划求出最大的面值;要求出邮票的面值,可以使用深度优先搜索算法。
题解
1 | /* |
Problem E
题目描述
某国为了防御敌国的导弹袭击,研发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试验阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入数据
输入数据只有一行,该行包含若干个数据,之间用半角逗号隔开,表示导弹依次飞来的高度(导弹最多有\(20\)枚,其高度为不大于\(3×10^3\)的正整数)。
输出数据
输出数据只有一行,该行包含两个数据,之间用半角逗号隔开。第一个数据表示这套系统最多能拦截的导弹数;第二个数据表示若要拦截所有导弹至少要再添加多少套这样的系统。
样例输入
1 | 389,207,155,300,299,170,158,65 |
样例输出
1 | 6,1 |
题解
1 |
|