最大和连续子序列(Hdu 1003)

算是写的第一篇技术博客吧,其实没想到要用这个来当做第一篇博客,今天去华为面试,出了这道题,我想了几分钟说这道题可以用动态规划,O(n)复杂度,结果面试官当场就说不可能,我说上课老师也有提到过,结果面试官来一句,那你上课肯定没有听课,然后没看我写的转移方程直接就略过了。于是我就变成了一个说大话,不听课,还不懂装懂的菜逼了,不用想,华为挂了。觉得很窝囊,之前上课老师的确提到过,在百度面的时候也出过,我当时就是这样写的都没问题。不过面试官的肯定的确让我自己都不确定了,回来查了一下,的确有O(n)的解法,而且和我的算法一模一样,如果是答错了还好想,但是答对了被干掉,的确不甘心,所以特意找来AC,也算是发泄一下了。


题目地址 http://acm.hdu.edu.cn/showproblem.php?pid=1003


题目大概就是说在一个数组里面,有正数负数,求和最大的连续子序列,题目很好理解。
一般的最容易想到的肯定是穷举(这也是我一开始给面试官说的),然而复杂度是O(n^2),当然还有一种O(n^3)的,见下面这个博客,说了四种,挺详细的。但是这篇博客里认为那种O(n)算法的方法只能求最大和,不能求起始和结束位置,其实是可以的,那位博主可能没想到,所以这就是我要贴的代码。

四种方法的最大和子序列:http://www.cnblogs.com/CCBB/archive/2009/04/25/1443455.html



思路大概和那位博主的第四种方法的思路一样的,那位博主在他的博客里面说明了,我就不用他的那种理解方式了,用的理解来方式来说明。用一个数组dp来存加到这一位的最大值,即dp[i]表示加了第i位的最大值,注意这里说的加上第i位其实就是两种情况:1.前面加上第i位,2从第i位起加上第i位(相当于起始位置从i位开始),所以转移方程就很容易了dp[i] = dp[i - 1] + array[i] > array[i] ? dp[i - 1] + array[i] : array[i] (这段代码就是说,谁大就用谁赋值,有的初学者可能看不懂这种表达,就和if else差不多,因为自己原来也是菜鸟过来的,所以就当是照顾以前的自己了)。
也可以这样理解这个方程,只要前面的序列加起来不为负数,那么当前这一位就可以加上前面的结果,必定比当前位本身大,如果前面加起来都是负数了,那就没有加起来的必要了,可以从当前位开始了。所以最后只要扫一遍这个数组,输出最大的那个dp[i]就是结果了。


所以要减少空间复杂度也很容易,不要dp,用一个变量cur_max来存上一次的dp[i]就好了,最大值max也可以找一个变量来存,这样就复杂度就降到O(1)了。就和那位博主的方法四一样了。


然而那位博主说不能记录起始位置和结束位置,起始也是可以的,设一个记录当前的起始和结束位置cur_beg,cur_end,设一个记录全局的起始和结束位置的变量beg,end,全局那个很好办,如果max < cur_max,那就更新全局的beg,end,并且更新max。当cur_max不为负的时候,cur_end往后移,如果cur_max为负了(就是要从当前位开始计算了),所以cur_beg = cur_end。所以就可以保证能够记录起始位和结束位了,符合这道题目的要求。


代码如下:


#include <iostream>
using namespace std;

int main(){
	int t;
	cin >> t;
	int count = 0;
	while(t--){
		count ++;
		int n;
		cin >> n;
		int * p = new int[n];
		for (int i = 0;i < n; ++i)
			cin >> p[i];
		int beg = 0,end = 0;
		int max = p[0];
		int cur_beg = 0,cur_end = 0,cur_max = p[0];
		for (int i = 1;i < n; ++i){
			if(cur_max < 0){
				cur_max = p[i];
				cur_beg = i;
				cur_end = i;
			}
			else {
				cur_max = cur_max + p[i];
				cur_end = i;
			}
			if(max < cur_max){
				max = cur_max;
				beg = cur_beg;
				end = cur_end;
			}
		}
		if(count != 1)cout << endl;
		cout << "Case " << count << ":"<< endl;
		cout << max << " " << beg + 1 << " " << end + 1 << endl;
		delete [] p;
	}
}

辅助空间复杂度O(1),时间复杂度O(n)

注意:貌似这道题要求每个样例之间间隔一行,但是最后一个样例后面是不用输出换行的,这里比较坑。


闲谈:其实今天的确感觉很郁闷,不是因为没有答出来被干掉,是答出来了被干掉了。后来想了一下,应该也不能怪华为那个面试官,毕竟太早面试了,可能面试官也未必非常清醒,或者人太多了,不能每个都记对,亦或是我自己误解了题目的意思,万一是另外一个题目呢,或者是其他方面表现不好才最终被干掉了呢。不过我想说的是,做任何事还是不要太绝对了,认为得理所当然,就像今天那个面试官完全十分肯定的说不可能有O(n)的算法,可能是认为自己掌握得很清楚了吧,也可能是其他原因。但这样未必好,人还是总得对世界有一份畏惧,这样才能看到自己的不足,能够慢慢进步吧。之前听一个宣讲会,主讲人也提过,即使是对这个领域了解得很深的人,也应该虚怀若谷,这样才能进一步提升。希望自己以后也能大概做到这一点吧。至于华为一事,我也已经尽力而为了,应该是很难有机会进入了,由于放弃了保研资格,更不会去考研,也不会有第二次校招给我了吧。

人事已尽,天意如此,何必强求,慢慢加油儿吧!

记 2015.10.11 广州华为面试


你可能感兴趣的:(面试,动态规划,最大和,华为,子序列)