序列相关的趣题 之中的一个

闲话少叙。直接上题。

(1) 最大子数组和

这个问题已经是toooooooooold了。

原问题是:给定一个数组。求一个子数组(连续的一段),它们的和最大。一些细节就是是否同意啥都不选,只是这个无关紧要。事实上不少书都把它作为动态规划的题目,我个人倒不太喜欢真的把它作为dp,之所以这么说,我认为作为dp反倒禁锢了人的思路。有点大材小用的感觉,而且真正的dp比这个复杂得多。这个题解法相当多。扔掉暴力的,也有非常多理解。

(1.1)我自己yy的。事实上和(1.2)是一样的。

连续的一段数我们能够把开头和结尾的负数扔掉,然后非负数连续的加在一起,负数加在一起,形成这种“堆”:

正 负 正 负 正 负……

之所以这样是由于我们同一堆里 要么就都选 要么就都不选,所以能够作为一个数处理。 (这是由于正数堆要选肯定都选了,负数堆要选肯定是为了“连接”到下一个正数上,否则选负数干嘛?)于是我们就一个一个看,先选第一个正的,假设加上负数还大于0,我们就继续选,也就是说仅仅要“前缀”是正的,我们就能够继续选数,前缀是负数的话,我们扔了重选。

事实上(1.2)就是这个思路。

(1.2) 经典解法  end[i]表示以a[i]结尾子数组的和。则end[i + 1] = max(end[i] + a[i + 1], a[i + 1]), 事实上这一步等价于end[i] >= 0 就取 end[i] + a[i + 1],否则就取a[i + 1],最优解肯定在某个地方结束。所以终于max{end[0..n - 1]}为所求。

习惯上把这种东西叫做动态规划……叫什么都好,反正这个确实满足最优子问题。即end[i + 1]是由end[i]推出来的。可是仅仅会这一种解法会影响理解后面的问题。

(1.3) 我个人非常喜欢的解法。由于体现了前缀和的思想。我们考虑prefix[i] = a[0] + a[1] + ...+a[i],即prefix[i]是一个前缀和。那么不论什么一个子数组就是两个前缀的差了。

细节是要定义prefix[-1] = 0。

那么子数组a[i..j] = prefix[j] - prefix[i - 1]。那么我们怎样求prefix[i]?

循环i就可以,prefix[i] = prefix[i - 1] + a[i]。那么怎样求(1.1)中的end[i]?要以a[i]结尾最大的,那么我们仅仅要用prefix[i] - prefix[j]就好了。而prefix[j]就是prefix[0..i]的最小值就能够了。所以我们循环i,每次用prefix[i]减去出现过的最小值就能够了……这是一个自然的思路。而且这个思路应用非常广泛……感觉尽管(1.2)是动态规划。可是我们不能过于重视。而忽视了(1.3)的存在。

(1.4) 仅仅有理论意义……由于是个O(nlogn)的方法。分治。从中间劈开数组。那么最大子数组要么在左半边。要么在右半边,这两部分递归解决。关键是最大子数组跨越中间点的情况。那么假设我们要求跨越中间点的最大和。我们就简单的从中间点往左扫一遍。看看连续加到哪里最大。相同往右边扫一边看看连续加到哪里最大就好了。这是由于我们确定了它一定要跨过中间点,所以有了一个“着力点”。主要是这部分的复杂度是O(n)的。所以整个递归分治算法的复杂度是O(nlogn)的。

顺便提一句,最优的算法(1.1)-(1.3)都是O(n)的。

(2) 同意交换一次的最大子数组和

比(1)难非常多,就是同意交换两个元素,仅仅交换一次。当然能够不交换,仍然是求最大子数组和。假设暴力枚举交换的两个元素,再求最大子数组和会到O(n^3)。尝试过先求普通最大子数组和,再贪心怎么换掉元素之类的。都能找到反例。这个题事实上有O(n)的算法,是个经典的dp,假设没理解(1.2)的话或者(1.2)靠死记硬背的话,无法解决问题。这是codility的challenge

http://blog.csdn.net/caopengcs/article/details/36899787

(3)  绝对值最小的子数组的和

就是求一个子数组,和的绝对值最小。

这是codility的练习……,这个无法线性解决。而且死记硬背(1)的话也没用,也不能套用。

这个须要用(1.3)的思路,子数组是两个前缀的差,那么我们对每一个前缀找到之前的最接近它的前缀就能够了。“最接近”包含比它小的最大的,和比它大的最小的,这个用一个set就能够攻克了。

stl有著名的lower_bound函数,就是logn时间完毕这个事,不然就要自己写二叉树了……

上个代码:

// you can write to stdout for debugging purposes, e.g.
// cout << "this is a debug message" << endl;

#include <set>

int solution(vector<int> &A) {
    // write your code in C++11
    set<int> have;
    have.insert(0);
    int sum = 0, r = 2000000000;
    for (int i = 0;r && (i < A.size()); ++i) {
        set<int>::iterator t = have.lower_bound(sum += A[i]);
        if (t != have.end()) {
            r = min(r, *t - sum);
        }
        if (t != have.begin()) {
            r = min(r, sum - *(--t));
        }
        have.insert(sum);
    }
    return r;
}
时间: 2024-10-17 04:59:37

序列相关的趣题 之中的一个的相关文章

序列相关的趣题 之四

(8) 给定一个英文单词,消除其中重复的字母,只能删掉字母,不能交换字母顺序,最后原单词中每个字母只出现一次,求字典序最小的结果. 这是toj一个题,百度面试也问过,原题见 http://acm.tju.edu.cn/toj/showp3257.html 此题我非常喜欢,巧妙之处是其算法是O(n)的-- .我们一个字母一个字母加入序列,一旦来了一个比较"小"的字母,因为我们需要字典顺序最小,我们希望它尽可能靠前.所以我们试图"冒泡"似的把小的往前面送,经过尾部那些较

序列相关的趣题 之二

(4)数组中找到两个数和的绝对值最小 像不像2-SUM? 不多解释,主要是绝对值大的动就行,两头扫的方法真好!当然要先排序,出去排序就是O(n),算上排序的话退化到O(nlogn) 这也是codility上的问题,还没来得及整理. 上个代码: // you can also use includes, for example: // #include <algorithm> #include <algorithm> int ab(int x) { return (x >= 0

算法题:求一个序列S中所有包含T的子序列(distinct sub sequence)

题: 给定一个序列S以及它的一个子序列T,求S的所有包含T的子序列.例: S = [1, 2, 3, 2, 4] T = [1, 2, 4] 则S的所有包含T的子序列为: [1, 2, 3, 2, 4] [1, 2, 3, 4] [1, 2, 2, 4] [1, 2, 4] 解: 首先可以拆解为两个问题: 1. 求S的所有子序列:其中又涉及到去重的问题. 2. 求S的所有子序列中包含T的子序列. 暂时先不考虑去重,看看问题1怎么解: 一.求S的子序列 单纯求一个序列的所有子序列的话,就是求序列的

uyhip 趣题 拉灯问题总有解吗?

这是一个让我纠结许久,又不甘放弃的puzzle.在一个意志力极度薄弱的下午,对不起,我看了答案...所以,这又是一篇马后炮文章.但不是所有马后炮都一文不值.如果在讲解一个解答的时候,我们不能把思考背后的动机讲清楚,于他人和自己的价值就会小很多.每一步推理的过程,每一个构造的细节,不是无迹可寻的.我希望去揭示背后的东西. 一个解答背后包含了大量的探索.解谜高手对于如何避免无效的思考,摸清靠谱的思路,总是有一套自己的办法.遗憾的是,好些同学由于各种原因,没有公开自己的方法.例如,高斯同学,他认为数学

算法趣题之回文数

题目:求用十进制.二进制.八进制表示都是回文数的所有数字中,大于十进制数10的最小值. 啥叫回文数:如果把某个十进制数按相反的顺序排列,得到的数和原来的数相同,则这个数就是"回文数".例如12321就是一个回文数. 这个题目拿Ruby.JavaScript.python.Java都很容易实现,因为这些语言都提供了字符串逆序处理的接口,或者相关其他接口,而C语言没有提供直接转换的接口,所以下面用C语言解题,其中设计的封装在工作中也会经常碰到,故记录并分享,如有错误或者有更好的算法,欢迎留

Python Special Syntax 4:序列相关

元组通过圆括号中用逗号分割的项目定义.元组通常用在使语句或用户定义的函数能够安全地采用一组值的时候,即被使用的元组的值不会改变. 含有0个或1个项目的元组.一个空的元组由一对空的圆括号组成,如myempty = ().然而,含有单个元素的元组就不那么简单了.你必须在第一个(唯一一个)项目后跟一个逗号,这样Python才能区分元组和表达式中一个带圆括号的对象.即如果你想要的是一个包含项目2的元组的时候,你应该指明singleton = (2 , ). 序列的切片功能: name = 'swaroo

使用Jquery+EasyUI进行框架项目开发案例解说之中的一个---员工管理源代码分享

使用Jquery+EasyUI 进行框架项目开发案例解说之中的一个 员工管理源代码分享 在開始解说之前,我们先来看一下什么是Jquery EasyUI?jQuery EasyUI是一组基于jQuery的UI插件集合,而jQuery EasyUI的目标就是帮助web开发人员更轻松的打造出功能丰富而且美观的UI界面.开发人员不须要编写复杂的javascript,也不须要对css样式有深入的了解,开发人员须要了解的仅仅有一些简单的html标签.jQuery EasyUI为我们提供了大多数UI控件的使用

【iOS与EV3混合机器人编程系列之四】iOS_WiFi_EV3_Library 剖析之中的一个:WiFi UDP和TCP

在上一篇文章中.我们通过编写EV3 Port Viewer项目实现了iOS监測EV3的实时端口数据. 程序最核心的部分就是我们的开源码库iOS_WiFi_EV3_Library. 那么,在本文中,我们将具体介绍我们这个库的编写.为了完毕这个库,本人參考了网上许多资料,主要包括EV3的源码,win版本号的代码库以及Monobrick相关以及网上的各种资料,在此就不一一列举了. 因为水平有限,本代码库还存在各种问题,望使用的读者见谅. 大家也能够在这个基础之上自己进行改造完好. 为了具体说明代码库的

输入阿拉伯数字(整数),输出相应的中文(美团网2014年9月16日笔试题目之中的一个)

2014年9月16日,美团网南京笔试题之中的一个.原要求是输入整数的位数最多为四位.这里扩展为12为,即最高到千亿级别. 思路及步骤: 1 判别输入是否合法,并过滤字符串最前面的'0'. 2 将字符串划分成四位一组的形式,当中每一组四位整数的输出方式同样.如20402040,其前四位和后四位都是2040.都输出"二千零四十",仅仅只是前四位要添上'万'字而已. 3 将8~12位.4~8位.0~4位的数字分成三种情况输出 另外:形如10230401,应读为:"一千零二十三零四百