【問題描述】
在長度為n的整形陣列中,求連續子串的和的最大值,要求複雜度為o(n)。
例如:1 2 3 -1 -20 100 34,結果為134。
【分析】
[思路一]
自己最初的想法,利用回溯的思想,從下標為0的元素開始遍歷,用former儲存當前最大的連續子集的和,當sum小於0的時候開始回溯,即刪除這個元素,刪除到已遍歷陣列最右邊的那個正整數為止(用下標j標識)。事後覺得這個演算法比較複雜,因為要維護下標j的指向,以及還要單獨求出陣列中的最大值,並最後與former進行比較。因此這個演算法只是實現了功能,此演算法的效率和**量都不好。
#include
#include
intmaxadjval(
inta,
intlen)
a[i]>0 ? flag=true
: null;
sum+=a[i];
if(sum<=0)
if(former
sum=0;// reset
} }
return
maxval>0 ? (sum>former? sum:former) : maxval;
} int
main()
, i=0, res=0;
printf("enter some numbers (ctrl+z to end):/n"
);
while
((1==scanf(
"%d"
,&a[i])) && ++i<100);
rewind(stdin);
res=maxadjval(a,i);
printf("%d/n"
,res);
return
0;
}
[思路二]
思路二對演算法進行了簡化,不採用回溯,只用maxsum儲存當前最大的連續子集的和,如果陣列全為負數,找乙個最大值就可以了。注意當cursum小於0的時候,要將cursum置為0。
乙個無序的陣列,找出相鄰的任意個元素,使得其和最大。要得o(n)的複雜度,順序讀取,任意步驟都是當前讀取的序列判斷最優解,此謂聯機演算法。(參考: )
用vector可以描述如下:
intmaxsubsum(
const
vector<
int>& vec)
return
maxsum;
}
用c語言描述如下:
#include
#include
intmaxadjval(
inta,
intlen)
return
max;
} int
main()
, i=0, res=0;
printf("enter some numbers (ctrl+z to end):/n"
);
while
((1==scanf(
"%d"
,&a[i])) && ++i<100);
rewind(stdin);
res=maxadjval(a,i);
printf("%d/n"
,res);
return
0;
}
【問題擴充套件】
1. 問這樣的子串有多少個。
2. 如果是首尾相連的,那麼最大子串和是多少,有多少個。
3. 如果是首尾相連的,取兩個不相交子串,那麼最大子串和是多少。
4. 若存在多個最大子串和,則輸出第乙個最大子串和的起始和終止下標。
【參考】
(1) 經典的dp問題。
請參考這道acm題:
#include
using
namespace
std;
void
maxsub(
int*arr,
intcount,
int&first,
int&last,
int∑);
intmain()
return
0;
} //求最大子串行之和,arr是原來的陣列,count是陣列的元素個數,
//first,last是所求子串行的第乙個元素,最後乙個元素的下標,sum是所求子串行的元素之和
void
maxsub(
int*arr,
intcount,
int&first,
int&last,
int∑)
//如果臨時的最大值大於實際的最大值,則更新子串行的first, last, sum
if(sum < tsum)
} }
(2) 一網友的幾種解決方法:
#include
using
namespace
std;
#define num 10
template
<
class
elemtype>
//普通2層迴圈時間複雜度 o(n*n)
void
maxsum(elemtype*p,
int& beg,
int& end,
int& sum)
} } }
//利用分治演算法時間複雜度o(n)
template
<
class
elemtype>
void
maxsum1(elemtype*p,
intlow,
inthigh,
int& beg,
int& end,elemtype& sum)
else
} elemtype sumr=0; //跨越左右區間的右邊部分的最大子段和
elemtype cursum1=0; //當前跨越左右區間的右邊區間部分的子段和
for(i=mid+1;i<=high;i++)
} sum=suml+sumr; //跨越左右區間的最大欄位和
if(sum<=leftsum)
if(sum<=rightsum)
} } //利用動態規劃時間複雜度也為o(n)
template
<
class
elemtype>
void
maxsum2(elemtype*p,
int& beg,
int& end,elemtype& sum)
else
if(b>sum)
//判斷當前子段和,之前最大子段和的大小
} } void
main()
; int
beg,end,sum;
beg=end=sum=0;
// maxsum(p,beg,end,sum);
// maxsum1(p,0,num-1,beg,end,sum);
maxsum2>(p,beg,end,sum);
cout<<"開始下標為:"
<
cout<<"結束下標為:"
<
cout<<"最大欄位和為:"
<
}
(3) 參考《程式設計之美》
(4) 關於此問題的討論。
DP 多段 「連續子段和」 的最大值問題
給一段連續的序列s1,s2,s3,s4 sx,sn 1 x n 1,000,000,32768 sx 32767 我們定義了sum i,j si sj 1 i j n 現在求的是 sum i1,j1 sum i2,j2 sum i3,j3 sum im,jm maximal ix iy jx or ...
求連續子串行的最大值
問題描述 有一串數字 可正可負的int,放在陣列num裡 要求找到起始位置start和終止位置end,使得從start位置到end位置的所有數字之和最大,返回這個最大值max。演算法思想 使用動態規劃。設 f x 為以 a x 終止且包含 a x 的最大序列的和,有 f 1 a 1 f x 1 f ...
面試經典(7) 連續子陣列的最大值
題目 輸入乙個整型陣列,陣列有整數也有負數,求其子陣列的最大值。演算法分析 微軟經典的一道題目。維持乙個當前累加的和cursum,如果cursum 0,那麼需要更改cursum為data i 這個很好理解,因為當前累加和是負數,是乙個累贅。如果cursum 0,那麼只需繼續累加data i 每次更改...