题目描述
分析
首先我们得知道什么是异或,这道题的核心就在于看到异或进行转移。
异或xor,^,A xor B就是再A,B两个数的二进制数中相同为0,不同为1。
一些定理:
0
x
o
r
A
=
A
;
0 xor A = A;
0xorA=A;
A
x
o
r
A
=
0
;
A xor A = 0;
AxorA=0;
A
x
o
r
(
B
x
o
r
C
)
=
(
A
x
o
r
B
)
x
o
r
C
;
A xor (B xor C) = (A xor B) xor C;
Axor(BxorC)=(AxorB)xorC;
因为这道题是的蜈蚣是一节一节的,并且是要求蜈蚣分为m段再异或后的最大值,不难想到设Dp[i][j]表示前i节蜈蚣分为j段能获得的最大值。让后我们可以利用xor的交换律对这个进行转移。
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
]
,
d
p
[
i
]
[
k
]
+
S
u
m
X
o
r
(
i
,
k
)
)
dp[i][j] = max(dp[i][j],dp[i][k] + SumXor(i,k))
dp[i][j]=max(dp[i][j],dp[i][k]+SumXor(i,k))
其中SumXor表示从i到k所有值异或起来的总和,我们可以设一个前缀和来表示前i个的异或总值,让后面DP时不用再一个一个地去求,以防T掉。dp的初始状态是dp[i][1] = sum[i],因为dp[i][1]就表示了i段分为1组的最大值,显然是他异或起来的本身,因为我们要求n段分为m组的最大值,所以说答案存在是dp[n][m].
十分简洁的的代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 1e3 + 5;
int n,m,w[MAXN],s[MAXN],dp[MAXN][MAXN];
int main() {
scanf("%d %d",&n,&m);
for(int i = 1;i <= n;i ++) {
scanf("%d",&w[i]);
s[i] = s[i - 1] ^ w[i];
}
for(int i = 1;i <= n;i ++) dp[i][1] = s[i];
for(int j = 2;j <= m;j ++)
for(int k = 1;k <= n;k ++)
for(int i = k;i <= n;i ++)
dp[i][j] = max(dp[i][j],dp[k][j - 1] + (s[i] ^ s[k]));
printf("%d",dp[n][m]);
return 0;
}
END