原題: http://acm.hdu.edu.cn/showproblem.php?pid=1003
看到這題的時候一臉懵逼,網上尋找解答,發現都講的不詳細,變量聲明也很短,根本看不出是什麼用途,這裏給出了我的解答,參考了網上各位大神的方法,加上了註釋,方便學習。
0x00 分治法解題
#include <stdio.h>
struct Sequence
{
//表示一個數組
int sum;//數組元素之和
int leftPosition;//數組左邊界位置
int rightPosition;//數組右邊界位置
};
/*
求最大子列:
首先明確:
給定一個數列
其最大子列的範圍,要麼包括正中間的位置,要麼全在最中間位置的左邊,要麼全在最中間位置的右邊
這三種情況在迭代函數中分別處理
*/
struct Sequence getMaxSequence(int * sourceSequence, int leftPosition, int rightPosition) {
// printf("into: %d, %d\n",leftPosition,rightPosition);
int mediumPosition;
//範圍:[leftPosition, rightPosition]
if (leftPosition == rightPosition) {
//給定範圍內的子數列只有一個元素,構造一個struct sequence來描述;
struct Sequence sequence;
sequence.leftPosition = leftPosition;
sequence.rightPosition = rightPosition;
sequence.sum = sourceSequence[leftPosition];
// printf("return: %d, %d\n",sequence.leftPosition,sequence.rightPosition);
return sequence;
//將這個元素返回
}
//取得中間位置
mediumPosition = (leftPosition + rightPosition) / 2;
//構造三種struct來保存三種情況的返回值;
struct Sequence leftSequence;
struct Sequence rightSequence;
struct Sequence aroundSequence;
leftSequence = getMaxSequence(sourceSequence, leftPosition, mediumPosition);
rightSequence = getMaxSequence(sourceSequence, mediumPosition + 1, rightPosition);
//第三種情況下,先從中間向左邊查找找出最大的和,再從中間向右邊查找找出最大的和,相加得到橫跨中間位置的最大值及範圍
//先是左邊
//先保存向左邊出發遇到的第一個值
int leftMaxSumTmp = sourceSequence[mediumPosition];
//並保存該值的位置
aroundSequence.leftPosition = mediumPosition;
int leftSumTmp = 0;
for (int index = mediumPosition; index >= leftPosition; index--) {
leftSumTmp = leftSumTmp + sourceSequence[index];
if (leftSumTmp >= leftMaxSumTmp) {
//如果發現從中間到左邊某一項的所有值之和比leftMaxSumTmp更大了,就更新leftMaxSumTmp
//並記錄此位置到aroundSequence的leftPosition字段
leftMaxSumTmp = leftSumTmp;
aroundSequence.leftPosition = index;
}
}
//再是右邊
//先保存向右邊出發遇到的第一個值(因爲到這裏leftPosition和rightPosition不相等所以meiumPosition + 1(一定小於等於rightPosition)一定沒有超過範圍:[leftPosition, rightPosition]),不加if
int rightMaxSumTmp = sourceSequence[mediumPosition + 1];
//保存該值的位置
aroundSequence.rightPosition = mediumPosition + 1;
int rightSumTmp = 0;
for (int index = mediumPosition + 1; index <= rightPosition; index++) {
rightSumTmp = rightSumTmp + sourceSequence[index];
if (rightSumTmp >= rightMaxSumTmp) {
//如果發現從中間到右邊某一項的所有值之和比rightMaxSumTmp更大了,就更新rightMaxSumTmp
//並記錄此位置到aroundSequence的rightPosition字段
rightMaxSumTmp = rightSumTmp;
aroundSequence.rightPosition = index;
}
}
aroundSequence.sum = leftMaxSumTmp + rightMaxSumTmp;
//三選一返回,選sum最長的那個返回
// printf("return: %d, %d\n",finalSequence.leftPosition,finalSequence.rightPosition);
return (leftSequence.sum > rightSequence.sum) ? ((leftSequence.sum > aroundSequence.sum) ? leftSequence : aroundSequence) : ((rightSequence.sum > aroundSequence.sum) ? rightSequence : aroundSequence);
}
int main() {
int numOfCase , indexOfCase = 1,lengthOfSquence;
scanf("%d", &numOfCase);
while (indexOfCase <= numOfCase) {
scanf("%d",&lengthOfSquence);
int sourceSequence[100000];
for(int index = 0;index <lengthOfSquence;index++){
scanf("%d",sourceSequence + index);
}
struct Sequence aimSequence = getMaxSequence(sourceSequence,0,lengthOfSquence - 1);
printf("Case %d:\n%d %d %d\n", indexOfCase ,aimSequence.sum,aimSequence.leftPosition + 1,aimSequence.rightPosition + 1);
if(indexOfCase != numOfCase){
printf("\n");
}
indexOfCase++;
}
return 0;
}
寒假看到這樣一篇文章,講的很詳細
http://conw.net/archives/9/#comment-25
0x01 動態規劃
#include <iostream>
#include <cstdio>
#include <cstring>
#define iinf 1e9
using namespace std;
// O(n)AC
// 動態規劃
// 原先維護一個dp數組,dp[i]保存以第i個位子結尾的所有子列的最大和
// dp[i] = list[i] + max(dp[i - 1], 0);
// 自處省略這個數組,用兩個變量來實現
int main(int argc, char const *argv[])
{
int T, N, last, now, ans, startpos, l, r;
scanf("%d", &T);
for (int j = 1; j <= T; j++) {
last = -1;
ans = -iinf;
l = 1;
r = 1;
scanf("%d", &N);
for (int i = 1; i <= N; i++) {
scanf("%d", &now);
if (last >= 0) {
last = now + last;
} else {
last = now;
startpos = i;
}
if (last > ans) {
ans = last;
l = startpos;
r = i;
}
}
if (j == 1) {
printf("Case 1:\n");
} else {
printf("\nCase %d:\n", j);
}
printf("%d %d %d\n", ans, l, r);
}
return 0;
}
0x02 結合優化後的前綴數組
用這種用法的人似乎更多
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define iinf 1e9
using namespace std;
// O(n)AC
// 另一種結合前綴數組的方法
// data[i] = list[0...(i - 1)];
// data數組從1開始避免對邊界的判斷
// 則list[x...y] = data[y + 1] - data[x];
// i作爲循環變量
// 維護data[i]的最小值mi,更新ans的最大值
// mi = min(mi, data[i])
// ans = max(data[i] - mi, ans)
// 可知若以第y個元素結尾的所有子列中list[x...y]爲最優解,那麼data[x]一定是data[1],data[2],data[3]...data[y](注意這裏沒有data[y+1],因爲至少要有一個元素)中的最小值
int main(int argc, char const *argv[])
{
int T, N, sum, now, ans, min, minpos, l, r;
scanf("%d", &T);
for (int j = 1; j <= T; j++) {
sum = 0;
ans = -iinf;
min = 0;
minpos = 0;
l = 1;
r = 1;
scanf("%d", &N);
for (int i = 1; i <= N; i++) {
scanf("%d", &now);
sum += now;
if (sum - min > ans) {//更新答案,這裏使用的是上一次循環的min值
r = i;
l = minpos + 1;
ans = sum - min;
}
if (sum < min) {//該判斷語句不可和上一句調換,否則將可能出現最優解的數列長度爲0;
min = sum;//更新本次循環的min,爲在下一循環中使用到
minpos = i;
}
}
if (j == 1) {
printf("Case 1:\n");
} else {
printf("\nCase %d:\n", j);
}
printf("%d %d %d\n", ans, l, r);
}
return 0;
}