在 oi-wiki 的贪心部分下面看到的这题。当然,之前做国王游戏的时候就知道了这道题,但是在当时国王游戏就已经是极限难度了,所以并没有深究。下午看了这道题,用要写博客这件事逼着自己完全理解,结果似乎因为划水所以花了很长时间......
首先,既然是皇后游戏,那么一定和国王游戏有联系(大胆猜测),考虑到国王游戏使用的是 邻项交换法,我们试着在这一题中也推出相应的式子。
不会LATEX,故手写(
设当前相邻的两位为 I,J
I 的左右手分别为a,b;J 的左右手分别为c,d
设 Σa1->i-1,设 h = ci-1
那么有
当 I 在前
当 J 在前
最后令 I,J 之中靠后的一方更小可以得到
也就是说,只要满足这个式子,我们得到的解就是最优的。
但是很显然,两边都 hbd 这一项。可以考虑去掉,只考虑剩下两项。因为
所以只要我们控制
就可以满足上述式子,这个式子相当于一个充分条件。化简这个式子
这样,这个式子中就只剩下和第 I,J 项有关的数据了。
然后,就像国王游戏那样,我们对所有大臣进行一个这样的排序,但是会发现,这样其实是不行的。很多题解都给出了一组 hack 数据,这里不做赘述,但是对于错误的原因要详细地说。
在对 I,J 进行比较的时候,我的定义是,如何让这两个大臣接在某个大臣之后,可以使得靠后的大臣价值更小。但是,不妨设想这样的一个场景。
在某个大臣后有三个大臣 A B C,此时他们的位置是 A B C。A 和 B 满足关系,B 和 C 满足关系,但是 A 和 C 不满足位置关系,也就是 C 应该在 A 的前面。使用 C++ 的 sort 之时,因为这种情况下相邻两项相相比都满足条件,程序便不再去改变这个序列,很容易导致错解。这要求我们设置的比较规则应该满足 传递性 这样,如果 A < B 且 B < C ,那么一定有 A < C,不会导致错解。
那么,我们思考一种方式,使得这种方式既有传递性,又可以满足上述式子 min(a,d)<=min(b,c)。
这里给出一种
将所有大臣分成三类,左手更小,左右手相同,右手更小,并且将这三种依此排列好。
对于同种大臣,左手更小的按照左手升序排列,右手更小的按照右手降序排列,左右手相同的随意排列。
容易证明,这样的比较方式具有传递性而且能满足上述式子(我是想不出来的)。
根据这样的方式 sort 一遍,然后按部就班求好答案就好了。
看懂思路就可以直接上手写了。细节...似乎也不是很多,集中在排序上。
#include<bits/stdc++.h> #define int long long using namespace std; inline void read(int&x) { x=0;bool f=1; char ch=getchar(); while(ch<48||ch>57){f=!(ch==45);ch=getchar();} while(ch>=48&&ch<=57){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} x=f?x:-x; } int T; struct node { int l,r; bool operator<(const node x)const { if(l<r&&x.l>=x.r)return 1; if(l>r&&x.l<=x.r)return 0; if(l==r&&x.l<x.r)return 0; if(l==r&&x.l>x.r)return 1; if(l<r&&x.l<x.r) { return l<x.l; } if(l>r&&x.l>x.r) { return r>x.r; } if(l==r&&x.l==x.r) { return l<x.l; } } }p[20001]; signed main(void) { read(T); while(T--) { int n;read(n); for(register int i=1;i<=n;++i) { read(p[i].l); read(p[i].r); } sort(p+1,p+n+1); int ans=p[1].l+p[1].r; int tot=p[1].l; for(register int i=2;i<=n;++i) { tot+=p[i].l; ans=max(ans,max(tot,ans)+p[i].r); } printf("%lld\n",ans); } return 0; }
对于邻项交换法的理解更深一层了。