P7958 [COCI2014-2015#6] NEO
题目大意
翻译的基本题面就不多说了,我们来大概分析一下题目。
- 序列会变回来:我们可以观察到,在 n n n 次变换后,序列会还原。也就是说,两个循环在同一个 i i i 上操作的序列是一样的。
- 下标的空间:然后我们再分析一下不难发现,下标是一大一小,也就是 min ( I D i , I D i + 1 ) \min\left(ID_{i},ID_{i+1}\right) min(IDi,IDi+1) 和 min ( I D i , I D i + 1 ) + 1 \min\left(ID_{i},ID_{i+1}\right)+1 min(IDi,IDi+1)+1,所以我们求 I D i ID_i IDi 时,去求 I D i − 1 ID_{i-1} IDi−1 就好了。聪明的小朋友想到动态规划了,那么再找找。
- 连续性:再找一找就可以发现就是选择一些边,那么就可以知道状态之间是关联的。
思路概述
经过了上面的思考,我们就不难可以发现,这道题肯定用动态规划。我总结了两种方法供大家食用:
强行 dp
这种思路是我一开始想出来的,其实挺好设的。我们就设
f
i
,
j
f_{i,j}
fi,j 表示在
i
i
i 的时候选
j
j
j 所能取到的最大贡献,所以我们就可以得到一个转移方程。
f
i
,
j
=
max
k
=
1
n
−
1
(
f
i
−
1
,
k
+
A
min
(
j
,
k
)
−
A
max
(
j
,
k
)
+
1
)
f_{i,j}=\max_{k=1}^{n-1}\left(f_{i-1,k}+A_{\min\left(j,k\right)}-A_{\max\left(j,k\right)}+1\right)
fi,j=k=1maxn−1(fi−1,k+Amin(j,k)−Amax(j,k)+1)
但是肯定有同学一眼丁真,发现时间复杂度太大了,所以我们优化成这个样子:
f
i
,
j
=
max
(
A
j
+
max
k
=
j
n
−
1
(
f
i
−
1
,
k
−
A
k
+
1
)
−
A
j
+
1
+
max
k
=
1
j
(
f
i
−
1
,
k
+
A
k
)
)
f_{i,j}=\max\left(A_j+\max_{k=j}^{n-1}\left(f_{i-1,k}-A_{k+1}\right)-A_{j+1}+\max_{k=1}^{j}\left(f_{i-1,k}+A_{k}\right)\right)
fi,j=max(Aj+k=jmaxn−1(fi−1,k−Ak+1)−Aj+1+k=1maxj(fi−1,k+Ak))
然后后面的东西我们可以使用前缀或者是后缀和搞定。然后我们上一下核心代码:
pre[0]=suf[n]=-1e9;
for(i=1;i<=n;++i,solve()){
for(k=1;k<n;++k){
if(pre[k-1]>=f[i-1][k]+A[k]){
pre[k]=pre[k-1];
pref[k]=pref[k-1];
}else{
pre[k]=f[i-1][k]+A[k];
pref[k]=k;
}
}
for(k=n-1;k;--k){
if(suf[k+1]>=f[i-1][k]-A[k+1]){
suf[k]=suf[k+1];
suff[k]=suff[k+1];
}else{
suf[k]=f[i-1][k]-A[k+1];
suff[k]=k;
}
}
for(j=1;j<n;++j){
int p=pre[j]-A[j+1],s=suf[j]+A[j];
if(p>=s){
f[i][j]=p;
trans[i][j]=pref[j];
}else{
f[i][j]=s;
trans[i][j]=suff[j];
}
}
}
但是,这个空间复杂度不够优秀,所以我们再换一种。
正解
那么我们可以对于每一个
i
i
i ,我们可以设
i
d
1
=
min
(
I
D
i
,
I
D
i
+
1
)
,
i
d
2
=
m
i
n
(
I
D
i
,
I
D
i
+
1
)
id_1=\min(ID_i,ID_{i+1}),id2=min(ID_i,ID_{i+1})
id1=min(IDi,IDi+1),id2=min(IDi,IDi+1)
所以,我们就可以得到
s
u
m
=
A
i
d
i
−
A
i
d
2
+
1
sum=A_{id_i}-A_{id_2+1}
sum=Aidi−Aid2+1。
同时,这也让我们想到了差分这件事情,所以我们可以构建出一个
(
n
−
1
)
×
n
\left(n-1\right)\times n
(n−1)×n 的矩阵,每一行都是旋转后记录的差分数组。但是动态规划的数组怎么设计呢?其实很简单,设
f
i
,
j
,
k
f_{i,j,k}
fi,j,k 表示走到了
(
i
,
j
)
\left(i,j\right)
(i,j) 这个位置的时候,方向是
k
k
k 的最长路径,所以就有如下的转移方程。
f
i
,
j
,
0
=
max
(
f
i
−
1
,
j
,
0
/
1
/
2
+
B
i
,
j
)
f
i
,
j
,
1
=
max
(
f
i
,
j
+
1
,
0
/
1
+
B
i
,
j
)
f
i
,
j
,
2
=
max
(
f
i
,
j
−
1
,
0
/
2
+
B
i
,
j
)
f_{i,j,0}=\max\left(f_{i-1,j,0/1/2}+B_{i,j}\right) f_{i,j,1}=\max\left(f_{i,j+1,0/1}+B_{i,j}\right) f_{i,j,2}=\max\left(f_{i,j-1,0/2}+B_{i,j}\right)
fi,j,0=max(fi−1,j,0/1/2+Bi,j)fi,j,1=max(fi,j+1,0/1+Bi,j)fi,j,2=max(fi,j−1,0/2+Bi,j)
代码就不贴了。