HDU1024 最大M子段和
HDU1024
不太明白这个问题这么难竟然这么多的AC,这个题卡了很久最后去找了题解。
首先说一下题意,是给一个长度为n的序列,要求从序列出m个不相交的子段,使他们的和最大
n<=1000000
我们首先想一下最暴力的DP方案
dp[i][j]表示选取第j个数字的情况下,将前j个数字分成i组的最大子段和
所以可能的情况有两种
①(x1,y1),(x2,y2)...(xi,yi,num[j])
①
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
.
.
.
(
x
i
,
y
i
,
n
u
m
[
j
]
)
②(x1,y1),(x2,y2)...(xi−1,yi−1),...,(num[j])
②
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
.
.
.
(
x
i
−
1
,
y
i
−
1
)
,
.
.
.
,
(
n
u
m
[
j
]
)
,其中yi-1是第k个数字
dp[i][j]=max(dp[i][j−1],dp[i−1][k])+num[j]其中k=[i−1,j−1]
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
−
1
]
[
k
]
)
+
n
u
m
[
j
]
其
中
k
=
[
i
−
1
,
j
−
1
]
首先发现dp[i][]只和dp[i-1][],dp[i]有关,所以这里可以滚动数组优化一下
dp[t][j]=max(dp[t][j−1],dp[1−t][k])+num[j]其中k=[i−1,j−1]
d
p
[
t
]
[
j
]
=
m
a
x
(
d
p
[
t
]
[
j
−
1
]
,
d
p
[
1
−
t
]
[
k
]
)
+
n
u
m
[
j
]
其
中
k
=
[
i
−
1
,
j
−
1
]
t=0||t=1分别表示当前状态和上一状态
t
=
0
|
|
t
=
1
分
别
表
示
当
前
状
态
和
上
一
状
态
我们发现最终我们只想要
max(dp[1−t][k])
m
a
x
(
d
p
[
1
−
t
]
[
k
]
)
,也就是上一个状态中的最大值,
所以我们用一个数组保存pre[j]就可以了,pre[j]表示不包括j的j之前的最大和
DP方程就变成了
dp[t][j]=max(dp[t][j−1],pre[j−1])+num[j]
d
p
[
t
]
[
j
]
=
m
a
x
(
d
p
[
t
]
[
j
−
1
]
,
p
r
e
[
j
−
1
]
)
+
n
u
m
[
j
]
所以这个时候t也是无用的了。
最终的状态转移方程就变为:
dp[j]=max(dp[j−1],pre[j−1])+num[j]
d
p
[
j
]
=
m
a
x
(
d
p
[
j
−
1
]
,
p
r
e
[
j
−
1
]
)
+
n
u
m
[
j
]
这道题充分考察了降维和滚动数组优化的巧妙性,要常回来品味一下
HDU1024代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn = 1e6+5;
int pre[maxn];
int dp[maxn];
int a[maxn];
int main()
{
int n,m;
while(scanf("%d%d",&m,&n)!=EOF)
{
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) pre[i]=0;
for(int i=1;i<=n;i++) dp[i]=0;
int ans=-INF;
for(int i=1;i<=m;i++)
{
ans=-INF;
for(int j=i;j<=n;j++)
{
dp[j]=max(dp[j-1],pre[j-1])+a[j];
pre[j-1]=ans;
ans=max(ans,dp[j]);
}
}
printf("%d\n",ans);
}
return 0;
}