http://acm.hdu.edu.cn/showproblem.php?pid=1024(到这里提交)
Max Sum Plus Plus
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4977 Accepted Submission(s): 1627
Given a consecutive number sequence
S1, S2, S3, S4 … Sx, … Sn(
1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). We define a function sum(i, j) =
Si + … + Sj (
1 ≤ i ≤ j ≤ n).
Now given an integer m (m > 0), your task is to find m pairs of i and j which make
sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + … + sum(im, jm) maximal (
ix ≤ iy ≤ jx or ix ≤ jy ≤ jx is not allowed).
But I`m lazy, I don’t want to write a special-judge module, so you don’t have to output m pairs of i and j, just output the maximal summation of sum(i
x, j
x)(
1 ≤ x ≤ m) instead. ^_^
1, S
2, S
3 … S
n.
Process to the end of file.
Huge input, scanf and dynamic programming is recommended.
最终程序代码:
代码
#include
<
stdio.h
>
2
int
m,n,a[
1000001
],ans;
3
int
f[
1000001
],sum[
1000001
],b[
2
][
1000001
];
4
int
main(){
5
int
i,j,k,i1,i2;
6
while
(scanf(
“
%d%d
“
,
&
m,
&
n)
!=
EOF)
7
{
8
sum[
0
]
=
0
;
9
for
(i
=
1
;i
<=
n;i
++
)
10
{
11
scanf(
“
%d
“
,
&
a[i]);
12
sum[i]
=
sum[i
–
1
]
+
a[i];
13
}
14
15
for
(j
=
0
;j
<=
n;j
++
)b[0][j]=0;————————前0个数分成j段的最大和为0
16
for ( j = 0; j <=n; j++) b[1][j]= –2000000000; //已改动
17
i1
=
1
;ans
=-
2000000000
;
18
19
for
(i
=
1
;i
<=
m;i
++
)
20
{ b[i1][j]每一次都应该初始化,因为空间是循环利用的
21
b[i1][i-1]
=
–
2000000000
; //已改动
22
for
(j
=
i;j
<=
n;j
++
)——————从数字数不少于分段数的地方开始
23
{
24
if
(i
==
j)f[j]
=
sum[j];
25
else
26
{
27
f[j]
=
f[j
–
1
]
+
a[j];
28
if
(f[j]
<
b[
1
–
i1][j
–
1
]
+
a[j])f[j]
=
b[
1
–
i1][j
–
1
]
+
a[j];
29
}
30
b[i1][j]
=
b[i1][j
–
1
];
31
if
(b[i1][j]
<
f[j])b[i1][j]
=
f[j];
32
}
33
i1
=
1
–
i1;—————i1总是0-1交替变化
34
}
35
for
(i
=
m;i
<=
n;i
++)——————此时f[i]中存储前i个数分成m段得到的最大和
36
if
(ans
<
f[i])ans
=
f[i];
37
printf(
“
%d\n
“
,ans);
38
}
39
return
0
;
40
}
此题难在两点:1.找到状态转移方程;2.优化程序。
状态转移方程:f[i][j]=max{f[i-1][j],max{f[i-1][k]}}+a[j]; (i-1=<k<=j-1)
f[i][j]代表将前j个数分成i段可以得到的最大和,并且这i段中必须包括a[j].它可以由两个状态转移到:a[j]与其他数一起包含
到第j段中,所得到的最大和(f[i-1][j]+a[j]);a[j]独立成段,与前i-1个数分成的j-1段得到的最大和(max{f[i-1][k]}+a[j])。
由于问题所涉及的规模实在太大,所以必须用滚动数组存储状态。同时设置sum[]数组,其中存储当i=j即分段数等于数字数时的
状态值,也就是所有j个数的和。主要代码见下:
代码
i1
=
1
;
2
ans
=-
(
1
<<
30
);
3
for
(i
=
1
;i
<=
m;i
++
)
4
{
5
for
(j
=
i;j
<=
n;j
++
)
6
{
7
if
(i
==
j)f[i1][j]
=
sum[j];
8
else
9
{
10
f[i1][j]
=
f[i1][j
–
1
]
+
a[j];
11
for(k=i-1;k<=j-1;k++
)
12
if(f[i1][j]<f[1-i1][k]+a[j])f[i1][j]=f[1-i1][k]+
a[j];
13
}
14
}
15
i1
=
1
–
i1;
16
}
17
j
=
m
%
2
;
18
for
(i
=
m;i
<=
n;i
++
)
19
if
(ans
<
f[j][i])ans
=
f[j][i];
20
printf(
“
%d\n
“
,ans);
红色代码区就是上述转移状态的第二个状态。这个代码的效率不高,很大程度上是因为这个循环。优化办法如下:
设置一个数组b[i][j],它是滚动式的,用来存储前j个数划为i段得到的最大和,但不一定包括a[j]。这样转移方程就可以变
为这样: f[i][j]=max{f[i-1][j],b[i-1][j-1]}+a[j];
b[i][j]=max{b[i][j-1],f[i][j]}
这样的话,在程序运行是就包含了两个转移方程,b[i][j]的值由两种状态转移可得:前j个数分为i段选a[j]得到的最大和(f[i][j]);
不选a[j]得到的最大和,即前j-1个数分为i段得到的最大和(b[i][j-1])。此时f[i][j]还可以省去一维,变为f[j].最终优
化后的程序见最上部,还有若干细节问题在程序旁会有注释。
转载于:https://www.cnblogs.com/aiyite826/archive/2010/07/24/1784449.html
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/110749.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...