让我们一起来%forever_shi神犇
题意:你有n个判断题,每题只可能是对或者错的一种,你事先知道了最终答案是对的题目数一定是下列若干种可能的某一个,求如何安排每一道题的选项能在最坏的情况下得到最多的分?
题解:
NOIP模拟赛的题目,被各位大神随便切,我却当场没想出来。
我们把正确答案是对号的题设为1,正确答案是错误的题设为0,这题首先是会发现,直接贪心地全选1或全选0并不一定是最优的,反例是在有8个题,最后可能是1道或7道题是1,那么你全选1或者全选0都是最多对1个,但是如果4个选1,4个选0就至少一定能对4个。
一下部分选自这里,我略作了注释。
首先,我们先提出一个引理:
对于两个长度为
n
n
n的01串,
A
A
A中
a
a
a个
1
1
1,
B
B
B中
b
b
b个
1
1
1,任一排列中相同位置元素相同的数量至少为
m
a
x
(
a
+
b
−
n
,
n
−
a
−
b
)
max(a+b−n,n−a−b)
max(a+b−n,n−a−b)。
证明:
1、
a
+
b
>
n
a+b>n
a+b>n,即
A
A
A和
B
B
B匹配
1
1
1,
A
A
A和
B
B
B中的
0
0
0全都和
1
1
1匹配了,考虑匹配了几个
1
1
1。
我们为了让相同元素尽可能少,考虑把
A
A
A中的
1
1
1全移到前端,
B
B
B中的
1
1
1全移到后端,变成线段覆盖问题,重合部分为
a
+
b
−
n
a+b−n
a+b−n。
2、
a
+
b
<
n
a+b<n
a+b<n,即
A
A
A和
B
B
B匹配
0
0
0。同理,我们考虑把
A
A
A中的
0
0
0全移到前端,
B
B
B中的
0
0
0全移到后端,用同样的方法得出该情况下结果为
n
−
a
−
b
n−a−b
n−a−b。
3、
a
+
b
=
n
a+b=n
a+b=n,显然答案为
0
0
0。
我们先将
t
t
t从小到大排序,设当前要匹配的
01
01
01串为
S
S
S,其中有
x
x
x个1。
对于每对
t
i
−
1
,
t
i
t_{i−1},t_i
ti−1,ti,我们肯定是拿
S
S
S和
t
i
−
1
t_{i−1}
ti−1去匹配
0
0
0,和
t
i
t_i
ti去匹配1。
由引理得
A
n
s
=
m
a
x
(
n
−
t
i
−
1
−
x
,
t
i
+
x
−
n
)
Ans=max(n−t_{i−1}−x,t_i+x−n)
Ans=max(n−ti−1−x,ti+x−n)。当
n
−
t
i
−
1
−
x
≥
t
i
+
x
−
n
n−t_{i−1}−x≥t_i+x−n
n−ti−1−x≥ti+x−n时,得
A
n
s
≥
(
t
i
−
t
i
−
1
)
/
2
Ans≥(t_i−t_{i−1})/2
Ans≥(ti−ti−1)/2。
同理当
n
−
t
i
−
1
−
x
≥
t
i
+
x
−
n
n−t_{i−1}−x≥t_i+x−n
n−ti−1−x≥ti+x−n时,得
A
n
s
≥
(
t
i
−
t
i
−
1
)
/
2
Ans≥(ti−t_{i−1})/2
Ans≥(ti−ti−1)/2。
综上所述,必有
A
n
s
≥
(
t
i
−
t
i
−
1
)
/
2
Ans≥(t_i−t_{i−1})/2
Ans≥(ti−ti−1)/2。
于是我们只需要给
t
t
t排好序之后扫一遍更新
a
n
s
ans
ans就行了。
最后还要判断是否全选
1
1
1或者全选
0
0
0会更优。
#include<bits/stdc++.h>
using namespace std;
int n,k,t[1001000];
int main()
{
cin>>n>>k;
for(int i=1;i<=k;++i)
scanf("%d",&t[i]);
sort(t+1,t+k+1);
int ans=max(t[1],n-t[k]);
for(int i=2;i<=k;++i)
ans=max(ans,(t[i]-t[i-1])/2);
cout<<ans;
return 0;
}