洛谷P2123 皇后游戏
贪心题目。对国王游戏的改动。
思路:
根据获得奖金的公式,可以知道每一位大臣获得的奖金总是比上一位大臣多,所以可以判断出最后一位大臣获得最多的奖金。
想知道最后一位大臣获得多少奖金,需要比较两个序列的和:
-
a [ 1 ] , a [ 2 ] . . . . . a [ n ] , b [ n ] a[1],a[2].....a[n],b[n] a[1],a[2].....a[n],b[n]
-
c [ n − 1 ] , b [ n ] c[n-1],b[n] c[n−1],b[n]
这就引出了下一个问题:需要知道第n-1位大臣的奖金。
还需要比较两个序列的和: -
a [ 1 ] , a [ 2 ] . . . . . . a [ n − 1 ] , b [ n − 1 ] , b [ n ] a[1],a[2]......a[n-1],b[n-1],b[n] a[1],a[2]......a[n−1],b[n−1],b[n]
-
c [ n − 2 ] , b [ n − 1 ] , b [ n ] c[n-2],b[n-1],b[n] c[n−2],b[n−1],b[n]
依此类推,需要比较到 a [ 1 ] , b [ 1 ] . b [ 2 ] . . . . . b [ n ] a[1],b[1].b[2].....b[n] a[1],b[1].b[2].....b[n]
即需要比较n个序列的和。
假设现在已经有了一个大臣的序列,我们考虑怎么交换大臣的位置能够减小这n个和中的最大值。
因为任意两个位置的交换可以通过交换相邻两个位置来达成,所以我们只考虑交换第i个和第i+1个位置的情况。
交换之前,两个有关的序列为: -
a [ 1 ] . . . . . a [ i − 1 ] , a [ i ] , b [ i ] , b [ i + 1 ] . . . . . . b [ n ] a[1].....a[i-1],a[i],b[i],b[i+1]......b[n] a[1].....a[i−1],a[i],b[i],b[i+1]......b[n]
-
a [ 1 ] . . . . . a [ i ] , a [ i + 1 ] , b [ i + 1 ] , b [ i + 2 ] . . . . . . b [ n ] a[1].....a[i],a[i+1],b[i+1],b[i+2]......b[n] a[1].....a[i],a[i+1],b[i+1],b[i+2]......b[n]
交换之后,两个序列为: -
a [ 1 ] . . . . . a [ i − 1 ] , a [ i + 1 ] , b [ i + 1 ] , b [ i ] , b [ i + 2 ] . . . . . . b [ n ] a[1].....a[i-1],a[i+1],b[i+1],b[i],b[i+2]......b[n] a[1].....a[i−1],a[i+1],b[i+1],b[i],b[i+2]......b[n]
-
a [ 1 ] . . . . . a [ i − 1 ] , a [ i + 1 ] , a [ i ] , b [ i ] , b [ i + 2 ] . . . . . . b [ n ] a[1].....a[i-1],a[i+1],a[i],b[i],b[i+2]......b[n] a[1].....a[i−1],a[i+1],a[i],b[i],b[i+2]......b[n]
如果交换之后,两个序列的和中的最大者大于等于之前两个序列的和中的最大者,那么我们是不要交换的。
即,如果下列条件:
(
a
[
i
+
1
]
−
a
[
i
]
>
=
0
∧
b
[
i
]
−
a
[
i
]
>
=
0
)
∨
(
a
[
i
+
1
]
−
b
[
i
+
1
]
>
=
0
∧
b
[
i
]
−
b
[
i
+
1
]
>
=
0
)
(a[i+1]-a[i]>=0 \wedge b[i]-a[i]>=0) \vee (a[i+1]-b[i+1]>=0 \wedge b[i]-b[i+1]>=0)
(a[i+1]−a[i]>=0∧b[i]−a[i]>=0)∨(a[i+1]−b[i+1]>=0∧b[i]−b[i+1]>=0)
中有一个成立,就不要交换。
即为:
a
[
i
]
<
=
m
i
n
(
a
[
i
+
1
]
,
b
[
i
]
)
∨
b
[
i
+
1
]
<
=
m
i
n
(
a
[
i
+
1
]
,
b
[
i
]
)
a[i]<=min(a[i+1],b[i]) \vee b[i+1]<=min(a[i+1],b[i])
a[i]<=min(a[i+1],b[i])∨b[i+1]<=min(a[i+1],b[i])
即为:
m
i
n
(
a
[
i
]
,
b
[
i
+
1
]
)
<
=
m
i
n
(
a
[
i
+
1
]
,
b
[
i
]
)
min(a[i],b[i+1])<=min(a[i+1],b[i])
min(a[i],b[i+1])<=min(a[i+1],b[i])
这个最终条件描述了正确序列具有的性质。
真的吗?
上面我做出了一个假设:任意两个位置的交换可以通过交换相邻两个位置来达成,所以我们只考虑交换第i个和第i+1个位置的情况。由于我觉得这是个很明显的事情,所以就没有多考虑。但是在这个问题里,这种想当然却造成了一场灾难。
为什么呢?因为上面这个不等式不具有传递性。
什么叫传递性?
你平时见过的从
a
<
=
b
,
b
<
=
c
a<=b,b<=c
a<=b,b<=c得到
a
<
=
b
<
=
c
a<=b<=c
a<=b<=c就是传递性的一个经典例子。
在这里,就是:
(
i
,
i
+
1
)
(i,i+1)
(i,i+1)和
(
i
+
1
,
i
+
2
)
(i+1,i+2)
(i+1,i+2)都满足了上述的不等关系,但是
(
i
,
i
+
2
)
(i,i+2)
(i,i+2)却不能满足上面的不等关系。
也就是说,我们真正的最终序列的正确性质:
m
i
n
(
a
[
i
]
,
b
[
j
]
)
<
=
m
i
n
(
a
[
j
]
,
b
[
i
]
)
(
i
<
j
)
min(a[i],b[j])<=min(a[j],b[i])\quad (i<j)
min(a[i],b[j])<=min(a[j],b[i])(i<j)
并不能从
m
i
n
(
a
[
i
]
,
b
[
i
+
1
]
)
<
=
m
i
n
(
a
[
i
+
1
]
,
b
[
i
]
)
min(a[i],b[i+1])<=min(a[i+1],b[i])
min(a[i],b[i+1])<=min(a[i+1],b[i])得到。
我也是看了别人的博客才意识到这个问题。(自己的水平还是太弱了,根本就没有深入思考…)
主要原因:错误的程序在洛谷上是能够AC的
从大佬那里借过来一个hack样例:
2
7
6 3
1 1
7 3
1 1
1 6
1 1
6 10
7
6 10
1 1
6 3
1 1
7 3
1 1
1 6
两组数据只是顺序不同,但是输出的结果却不一样。
还有下面这个例子:
7 3
1 1
1 6
结果:17
1 1
1 6
7 3
结果:12
两组数据顺序不同,内容完全一样,但是输出了不同的结果。你会发现两组数据都满足上面的相邻不等式,但是只有第二组才有传递性。
现在最关键的问题变成了:如何让我们的最终关系具有传递性,保证排序后数组中的每两组元素都满足上面的不等式。
我们从观察这个条件入手:
( a [ i + 1 ] − a [ i ] > = 0 ∧ b [ i ] − a [ i ] > = 0 ) ∨ ( a [ i + 1 ] − b [ i + 1 ] > = 0 ∧ b [ i ] − b [ i + 1 ] > = 0 ) (a[i+1]-a[i]>=0 \wedge b[i]-a[i]>=0) \vee (a[i+1]-b[i+1]>=0 \wedge b[i]-b[i+1]>=0) (a[i+1]−a[i]>=0∧b[i]−a[i]>=0)∨(a[i+1]−b[i+1]>=0∧b[i]−b[i+1]>=0)
可以注意到, a [ i ] a[i] a[i]和 b [ i ] b[i] b[i]的关系在这个不等式中是很重要的。因为:
- 如果 a [ i ] < b [ i ] ∧ a [ i + 1 ] < b [ i + 1 ] a[i]<b[i] \wedge a[i+1]<b[i+1] a[i]<b[i]∧a[i+1]<b[i+1] 那么必定有 a [ i + 1 ] > = a [ i ] a[i+1]>=a[i] a[i+1]>=a[i], a a a应该升序排列
- 如果 a [ i ] > b [ i ] ∧ a [ i + 1 ] > b [ i + 1 ] a[i]>b[i] \wedge a[i+1]>b[i+1] a[i]>b[i]∧a[i+1]>b[i+1] 那么必定有 b [ i + 1 ] < = b [ i ] b[i+1]<=b[i] b[i+1]<=b[i], b b b应该降序排列
- 如果 a [ i ] = b [ i ] ∧ a [ i + 1 ] = b [ i + 1 ] a[i]=b[i] \wedge a[i+1]=b[i+1] a[i]=b[i]∧a[i+1]=b[i+1] 你就可以随便乱排
这样,我们就可以按照a和b的大小关系把这个序列分成三类。按照上面的讨论,在相同的类别中, m i n ( a [ i ] , b [ i + 1 ] ) < = m i n ( a [ i + 1 ] , b [ i ] ) min(a[i],b[i+1])<=min(a[i+1],b[i]) min(a[i],b[i+1])<=min(a[i+1],b[i])是具有传递性的。
为了方便,我们引入 d [ i ] = s g n ( a [ i ] − b [ i ] ) d[i]=sgn(a[i]-b[i]) d[i]=sgn(a[i]−b[i]),其中
s g n ( x ) = { 1 , x > 0 0 , x = 0 − 1 , x < 0 sgn(x)=\left\{\begin{matrix} 1,x>0\\ 0,x=0\\ -1,x<0 \end{matrix}\right. sgn(x)=⎩⎨⎧1,x>00,x=0−1,x<0
我们要做的,是首先按照
d
d
d值分类,以保证同类元素中的关系具有传递性。
那么,不同类别之间的元素怎么办呢?
我们考察 d [ i ] d[i] d[i]和 d [ j ] d[j] d[j],并且假设 d [ i ] < = d [ j ] d[i]<=d[j] d[i]<=d[j]。
也就是, a [ i ] < = b [ i ] a[i]<=b[i] a[i]<=b[i] 并且 a [ j ] > = b [ j ] a[j]>=b[j] a[j]>=b[j]
此时我们会发现,不等式一定是满足的。为什么呢?
观察:
(
a
[
j
]
−
a
[
i
]
>
=
0
∧
b
[
i
]
−
a
[
i
]
>
=
0
)
∨
(
a
[
j
]
−
b
[
i
+
1
]
>
=
0
∧
b
[
i
]
−
b
[
j
]
>
=
0
)
(a[j]-a[i]>=0 \wedge b[i]-a[i]>=0) \vee (a[j]-b[i+1]>=0 \wedge b[i]-b[j]>=0)
(a[j]−a[i]>=0∧b[i]−a[i]>=0)∨(a[j]−b[i+1]>=0∧b[i]−b[j]>=0)会发现,满足上述条件后,要想违反不等式,必须满足
a
[
j
]
<
a
[
i
]
∧
b
[
i
]
<
b
[
j
]
a[j]<a[i] \wedge b[i]<b[j]
a[j]<a[i]∧b[i]<b[j]
但是,这样的话会有 a [ i ] < = b [ i ] < b [ j ] < = a [ j ] a[i]<=b[i]<b[j]<=a[j] a[i]<=b[i]<b[j]<=a[j],是矛盾的。
所以,在 d [ i ] < = d [ j ] d[i]<=d[j] d[i]<=d[j]时,我们的条件不等式必定成立。
那么,我们在排序的时候,只要先把d值小的元素放到前面就可以了。
下面是真正的代码:
# include <bits/stdc++.h>
using namespace std ;
typedef long long LL ;
struct node {
int a,b ;
int d ;
}per[20010];
bool cmp( const node &x , const node &y ){
if( x.d != y.d ) return x.d < y.d ;
else {
if( x.d >= 0 ) return x.b > y.b ;
else return x.a < y.a ;
}
}
int sgn(int x){
if( x>0 ) return 1 ;
else if( x==0 ) return 0 ;
else return -1 ;
}
void init(){
int n ;
scanf("%d" , &n ) ;
LL ans=0 , sum=0 ;
for( int i=0 ; i<n ; i++ ){
scanf("%d%d" , &per[i].a , &per[i].b ) ;
per[i].d = sgn(per[i].a-per[i].b) ;
}
sort(per,per+n,cmp) ;
ans = per[0].a + per[0].b ;
sum = per[0].a ;
for( int i=1 ; i<n ; i++ ){
sum += per[i].a ;
ans = max( ans , sum ) + (LL)per[i].b ;
}
printf("%lld\n" , ans ) ;
return ;
}
int main () {
int T ;
scanf("%d" , &T ) ;
for( int i=0 ; i<T ; i++ ){
init() ;
}
return 0 ;
}
至此,这道题目才算是真正的完结。