洛谷P2123 皇后游戏

洛谷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[n1],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[n1],b[n1],b[n]

  • c [ n − 2 ] , b [ n − 1 ] , b [ n ] c[n-2],b[n-1],b[n] c[n2],b[n1],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[i1],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[i1],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[i1],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]>=0b[i]a[i]>=0)(a[i+1]b[i+1]>=0b[i]b[i+1]>=0)
中有一个成立,就不要交换。
即为: a [ i ] &lt; = m i n ( a [ i + 1 ] , b [ i ] ) ∨ b [ i + 1 ] &lt; = m i n ( a [ i + 1 ] , b [ i ] ) a[i]&lt;=min(a[i+1],b[i]) \vee b[i+1]&lt;=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 ] ) &lt; = m i n ( a [ i + 1 ] , b [ i ] ) min(a[i],b[i+1])&lt;=min(a[i+1],b[i]) min(a[i],b[i+1])<=min(a[i+1],b[i])
这个最终条件描述了正确序列具有的性质。

真的吗?

上面我做出了一个假设:任意两个位置的交换可以通过交换相邻两个位置来达成,所以我们只考虑交换第i个和第i+1个位置的情况。由于我觉得这是个很明显的事情,所以就没有多考虑。但是在这个问题里,这种想当然却造成了一场灾难。

为什么呢?因为上面这个不等式不具有传递性。
什么叫传递性?
你平时见过的从 a &lt; = b , b &lt; = c a&lt;=b,b&lt;=c a<=b,b<=c得到 a &lt; = b &lt; = c a&lt;=b&lt;=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 ] ) &lt; = m i n ( a [ j ] , b [ i ] ) ( i &lt; j ) min(a[i],b[j])&lt;=min(a[j],b[i])\quad (i&lt;j) min(a[i],b[j])<=min(a[j],b[i])(i<j)
并不能从 m i n ( a [ i ] , b [ i + 1 ] ) &lt; = m i n ( a [ i + 1 ] , b [ i ] ) min(a[i],b[i+1])&lt;=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 ] &gt; = 0 ∧ b [ i ] − a [ i ] &gt; = 0 ) ∨ ( a [ i + 1 ] − b [ i + 1 ] &gt; = 0 ∧ b [ i ] − b [ i + 1 ] &gt; = 0 ) (a[i+1]-a[i]&gt;=0 \wedge b[i]-a[i]&gt;=0) \vee (a[i+1]-b[i+1]&gt;=0 \wedge b[i]-b[i+1]&gt;=0) (a[i+1]a[i]>=0b[i]a[i]>=0)(a[i+1]b[i+1]>=0b[i]b[i+1]>=0)

可以注意到, a [ i ] a[i] a[i] b [ i ] b[i] b[i]的关系在这个不等式中是很重要的。因为:

  1. 如果 a [ i ] &lt; b [ i ] ∧ a [ i + 1 ] &lt; b [ i + 1 ] a[i]&lt;b[i] \wedge a[i+1]&lt;b[i+1] a[i]<b[i]a[i+1]<b[i+1] 那么必定有 a [ i + 1 ] &gt; = a [ i ] a[i+1]&gt;=a[i] a[i+1]>=a[i] a a a应该升序排列
  2. 如果 a [ i ] &gt; b [ i ] ∧ a [ i + 1 ] &gt; b [ i + 1 ] a[i]&gt;b[i] \wedge a[i+1]&gt;b[i+1] a[i]>b[i]a[i+1]>b[i+1] 那么必定有 b [ i + 1 ] &lt; = b [ i ] b[i+1]&lt;=b[i] b[i+1]<=b[i] b b b应该降序排列
  3. 如果 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 ] ) &lt; = m i n ( a [ i + 1 ] , b [ i ] ) min(a[i],b[i+1])&lt;=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 &gt; 0 0 , x = 0 − 1 , x &lt; 0 sgn(x)=\left\{\begin{matrix} 1,x&gt;0\\ 0,x=0\\ -1,x&lt;0 \end{matrix}\right. sgn(x)=1,x>00,x=01,x<0

我们要做的,是首先按照 d d d值分类,以保证同类元素中的关系具有传递性。
那么,不同类别之间的元素怎么办呢?

我们考察 d [ i ] d[i] d[i] d [ j ] d[j] d[j],并且假设 d [ i ] &lt; = d [ j ] d[i]&lt;=d[j] d[i]<=d[j]

也就是, a [ i ] &lt; = b [ i ] a[i]&lt;=b[i] a[i]<=b[i] 并且 a [ j ] &gt; = b [ j ] a[j]&gt;=b[j] a[j]>=b[j]

此时我们会发现,不等式一定是满足的。为什么呢?

观察:
( a [ j ] − a [ i ] &gt; = 0 ∧ b [ i ] − a [ i ] &gt; = 0 ) ∨ ( a [ j ] − b [ i + 1 ] &gt; = 0 ∧ b [ i ] − b [ j ] &gt; = 0 ) (a[j]-a[i]&gt;=0 \wedge b[i]-a[i]&gt;=0) \vee (a[j]-b[i+1]&gt;=0 \wedge b[i]-b[j]&gt;=0) (a[j]a[i]>=0b[i]a[i]>=0)(a[j]b[i+1]>=0b[i]b[j]>=0)会发现,满足上述条件后,要想违反不等式,必须满足 a [ j ] &lt; a [ i ] ∧ b [ i ] &lt; b [ j ] a[j]&lt;a[i] \wedge b[i]&lt;b[j] a[j]<a[i]b[i]<b[j]

但是,这样的话会有 a [ i ] &lt; = b [ i ] &lt; b [ j ] &lt; = a [ j ] a[i]&lt;=b[i]&lt;b[j]&lt;=a[j] a[i]<=b[i]<b[j]<=a[j],是矛盾的。

所以,在 d [ i ] &lt; = d [ j ] d[i]&lt;=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 ;
}

至此,这道题目才算是真正的完结。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值