题目
https://codeforces.com/gym/102920/problem/E
有两轮操作,每一轮,让 1 ∼ n 1\sim n 1∼n中的每个数字 k k k和其他所有数字进行比较大小,如果两个数字之差大于等于 2 2 2则能正确的比较大小,否则随机认为某个数字大,令 r i ( k ) r_i(k) ri(k)为第 i i i轮时比 k k k小的数字的个数,则 d k = ∣ r 1 ( k ) − r 2 ( k ) ∣ d_k=|r_1(k)-r_2(k)| dk=∣r1(k)−r2(k)∣,现在给定 d d d数组,问你经过两轮操作后是否可能出现 d d d数组这种情况。
思路
可以发现,
d
k
d_k
dk的值只和
k
−
1
k-1
k−1和
k
+
1
k+1
k+1有关,因为
k
k
k和其他数字比较都是确定的,相减贡献就抵消了。令
a
k
a_k
ak表示
k
k
k和
k
−
1
k-1
k−1比较时对差的贡献,
b
k
b_k
bk表示
k
k
k和
k
+
1
k+1
k+1比较时对差的贡献,则
d
k
=
∣
a
k
+
b
k
∣
d_k=|a_k+b_k|
dk=∣ak+bk∣,
−
1
≤
a
k
,
b
k
≤
1
-1\le a_k,b_k\le 1
−1≤ak,bk≤1,还可以发现
a
k
+
b
k
+
1
=
0
a_k+b_{k+1}=0
ak+bk+1=0,
b
k
+
a
k
+
1
=
0
b_k+a_{k+1}=0
bk+ak+1=0。可以考虑
d
p
dp
dp,令
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示考虑前
i
i
i个数字,
b
k
=
j
b_k=j
bk=j时,
d
d
d数组是否符合要求,然后
a
k
=
{
d
k
−
j
−
d
k
−
j
a_k=\begin{cases} d_k-j\\ -d_k-j \end{cases}
ak={dk−j−dk−j
如果
−
1
≤
a
k
≤
1
-1\le a_k\le 1
−1≤ak≤1,那么就可以继续算出
b
k
−
1
=
−
a
k
b_{k-1}=-a_k
bk−1=−ak,于是
d
p
[
i
]
[
j
]
=
d
p
[
i
]
[
j
]
∣
∣
d
p
[
i
−
1
]
[
b
k
−
1
]
dp[i][j]=dp[i][j]||dp[i-1][b_{k-1}]
dp[i][j]=dp[i][j]∣∣dp[i−1][bk−1]
因为
b
n
=
0
b_n=0
bn=0,所以答案就是
d
p
[
n
]
[
0
]
dp[n][0]
dp[n][0]。
#include<bits/stdc++.h>
using namespace std;
const int N=1000009;
int n,a[N],dp[N][3],b[3]= {0,1,-1};
map<int,int>ma;
int main() {
ma[0]=0;
ma[1]=1;
ma[-1]=2;
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
dp[0][0]=1;
for(int i=1; i<=n; i++)
for(int j=0; j<3; j++) {
int tmp=b[j];
if(a[i]-tmp>=-1&&a[i]-tmp<=1)
dp[i][j]=dp[i][j]||dp[i-1][ma[tmp-a[i]]];
if(-a[i]-tmp>=-1&&-a[i]-tmp<=1)
dp[i][j]=dp[i][j]||dp[i-1][ma[tmp+a[i]]];
}
if(dp[n][0]){
printf("YES");
return 0;
}
printf("NO");
return 0;
}