【算法30】从数组中选择k组长度为m的子数组,要求其和最小
原题链接:codeforce 267 Div2 C
问题描述:
给定长度为n的数组a[],从中选择k个长度为m的子数组,要求和最大。
形式描述为:选择$k$个子数组[$l_1$, $r_1$], [$l_2$, $r_2$], ..., [$l_k$l1, $r_k$] (1 ≤ $l_1$ ≤$r_1$ ≤$l_2$ ≤ $r_2$ ≤... ≤$l_k$ ≤ $r_k$ ≤ n; $r_i-r_i+1$), 使得$\sum_{i=1}^{k}\sum_{j=l_i}^{r_i}p_j$
问题分析:
【思路1】先从简单粗暴的方法入手,怎么办?寻找所有的k个长度为m的子数组,然后选择其中和最小的。第一个长度为m的子数组开始位置可能为0...(k-1)*m,然后第二个子数组的下标?第三个子数组下标?太复杂了而且时间复杂度肯定超高,不能忍,换个方法吧。
【思路2】再看一下问题,要求和最大,求最值问题十有八九都是DP问题,试试吧。DP题目子问题怎么定义是关键,然后这东西基本只能靠经验了(嗯,算法导论上就是这么说的)。从后往前考虑,那么对于最后一个元素,只有两种情况,被选中到子数组中或者没有被选到子数组中。如果被选中,那么首先计算最后m个元素的和,剩下的问题就化为从前面长度为n-m的数组中选择k-1组和最大的子数组。如果没选中最后一个,也好办,直接转化为从前面n-1个元素中选择k组和最大的子数组。分析后我们有:
子问题定义: dp[i][j] = 从前i个元素中选择j个子数组的最大和
状态转移方程: dp[i][j] = max(dp[i-1][j], dp[i-m][j-1] + sum(a[i-m]...a[i-1]))
初始条件: dp[0][j] = 0; dp[i][0] = 0; if (i < j * m) dp[i][j] = 0;
AC代码如下:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>
using namespace std; int main()
{
int n, m, k;
cin >> n >> m >> k; vector<int> v(n, );
for (int i = ; i < n; ++i)
{
cin >> v[i];
} // dp[i][j] = choose j pairs integers from the first i elements
// Then base on the ith is chosen or not, there are two case:
// not choose ith element, the dp[i][j] = dp[i-1][j]
// choose ith element, the dp[i][j] = dp[i-m][j-1] + sum(a[i-1]...a[i-m])
// so dp[i][j] = max(dp[i-1][j], dp[i-m][j-1] + sum(a[i-1]...a[i-m])
// base case: assert (i >= j * m) if not 0 dp[i][j] = 0
// the problem is equal to find dp[n][k] vector<vector<long long> > dp(n+, vector<long long>(k+, )); // base case
for (int i = ; i < n + ; ++i)
{
for (int j = ; j < k + ; ++j)
{
if (i < j * m)
{
dp[i][j] = ;
}
}
} // bottom to up
for (int i = ; i < n + ; ++i)
{
for (int j = ; j < k + ; ++j)
{
if (i >= j * m)
{
long long lastPairSum = accumulate(v.begin() + i - m, v.begin() + i, 0LL);
dp[i][j] = max(dp[i-][j], dp[i-m][j-] + lastPairSum);
} }
} long long ans = dp[n][k];
cout << ans << endl;
return ;
}
注意点:
这道题目很简单的,为什么要记录下来呢,因为我用了int,出现了overflow,想了半天也没想明白到底错在哪里了,脑子真是瓦特啦,记下来以免重蹈覆辙。
【算法30】从数组中选择k组长度为m的子数组,要求其和最小的更多相关文章
- 选择问题(选择数组中第K小的数)
由排序问题可以引申出选择问题,选择问题就是选择并返回数组中第k小的数,如果把数组全部排好序,在返回第k小的数,也能正确返回,但是这无疑做了很多无用功,由上篇博客中提到的快速排序,稍稍修改下就可以以较小 ...
- 现在有m组n个有序数组,例如{1,2,3,4},{2,3,4,6},{1,3,5,7},在这些数组中选择第k小的数据,然后返回这个值
问题描述:现在有m组n个有序数组,例如{1,2,3,4},{2,3,4,6},{1,3,5,7},在这些数组中选择第k小的数据,然后返回这个值 思路:参照两个数组归并的过程,每次选取最小的数据进行比较 ...
- [经典算法题]寻找数组中第K大的数的方法总结
[经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26 字体:[大 中 小] 打印复制链接我要评论 今天看算法分析是,看到一个这样的问题,就是在一堆数据 ...
- 前端算法题:找出数组中第k大的数字出现多少次
题目:给定一个一维数组,如[1,2,4,4,3,5],找出数组中第k大的数字出现多少次. 例如:第2大的数是4,出现2次,最后输出 4,2 function getNum(arr, k){ // 数组 ...
- 查找数组中第k大的数
问题: 查找出一给定数组中第k大的数.例如[3,2,7,1,8,9,6,5,4],第1大的数是9,第2大的数是8-- 思考:1. 直接从大到小排序,排好序后,第k大的数就是arr[k-1]. 2. ...
- 寻找数组中第K大数
1.寻找数组中的第二大数 using System; using System.Collections.Generic; using System.Linq; using System.Text; u ...
- 一题多解(五) —— topK(数组中第 k 大/小的数)
根据对称性,第 k 大和第 k 小,在实现上,是一致的,我们就以第 k 小为例,进行说明: 法 1 直接排序(sort(A, A+N)),当使用一般时间复杂度的排序算法时,其时间复杂度为 O(N2) ...
- [LeetCode] Kth Largest Element in an Array 数组中第k大的数字
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the so ...
- 数组中第K小的数字(Google面试题)
http://ac.jobdu.com/problem.php?pid=1534 题目1534:数组中第K小的数字 时间限制:2 秒 内存限制:128 兆 特殊判题:否 提交:1120 解决:208 ...
随机推荐
- MySQL+Navicat for MySQL安装
一.安装MySQL 1.下载MySQL http://cdn.mysql.com//Downloads/MySQL-5.7/mysql-5.7.10-winx64.zip 2.安装 2.1解压安装包 ...
- Python oct() 函数
Python oct() 函数 Python 内置函数 描述 oct() 函数将一个整数转换成8进制字符串. 语法 oct 语法: oct(x) 参数说明: x -- 整数. 返回值 返回8进制字符 ...
- Water Tree(树链剖分+dfs时间戳)
Water Tree http://codeforces.com/problemset/problem/343/D time limit per test 4 seconds memory limit ...
- 128. Longest Consecutive Sequence最长连续序列
[抄题]: Given an unsorted array of integers, find the length of the longest consecutive elements seque ...
- dump()
输出格式化的对象
- IPEndPoint
.NET框架用IPEndPoint 对象来表示一个特定的IP地址和端口的组合,应用该对象的场景多是在讲socket绑定到本地地址或者将socket绑定到非本地地址.
- 解决Axure发布分享预览的3个方法
公司的同事制作的一个产品原型,要发给我,我当时正在客户这里,电脑上并没有Axure,客户又催得急,感到一阵无奈.这次回来之后,经过一番摸索,发现还是有办法的.这里给大家分享一下Axure发布分享预览的 ...
- myeclipse svn 插件去除已经保存的密码方法
myeclipse svn 插件去除已经保存的密码方法 删除掉C:\Documents and Settings\hao\Application Data\Subversion\auth\svn. ...
- 2018.09.24 codeforces 1053C. Putting Boxes Together(线段树)
传送门 就是让你维护动态的区间带权中位数. 然而昨晚比赛时并没有调出来. 想找到带权中位数的中点可以二分(也可以直接在线段树上找). 也就是二分出第一个断点,使得断点左边的和恰好大于或等于断点右边的和 ...
- 27. Green Building 绿色建筑
27. Green Building 绿色建筑 ①When we think of green buildings,we tend to think of new ones-the kind of h ...