1. 程式人生 > 實用技巧 >皇后遊戲

皇后遊戲

link

填填初二時研究過的坑(今天初二考試題)。相鄰交換法的應用。

跳過所有前面的簡化式子,從 $\min\{A_i,B_j\}$ 與 $\min\{A_j,B_i\}$ 分析。

若對於所有 $i<j$ ,需要均滿足$\min\{A_i,B_j\} \leq \min\{A_j,B_i\}$ 即可。

但是這個東西可以用來寫 $cmp$ 函式嗎,答案是不能的。因為 $sort$ 時 $cmp$ 需要滿足是嚴格弱序的。

嚴格弱序需要滿足:

非對稱性:$A<B \land B<A$ 從不成立

傳遞性:$A<B\land B<C$ 可以推出 $A<C$

不可比性的傳遞性:令 $A!<B\land B!<A\land B!<C\land C!<B$ 可以推出 $A!<C\land C!<A$

即通過條件唯一確定一個序列的順序。

而對於上面的比較不滿足不可比傳遞性,如 $(10,10),(6,6),(7,9)$。

對於之前的國王遊戲最後推出的是 $\dfrac{a_i}{b_i}<\dfrac{a_j}{b_j}$ ,這個東西明顯具有嚴格弱序,故可以直接排序處理。

而我們如何去處理這道題呢。

有一個很便捷的做法是對於 $i,j$ 通過$\min\{A_i,B_j\}$ 與 $\min\{A_j,B_i\}$ 建圖(需要滿足可以相鄰交換),按照其拓撲序排列即可。時間複雜度 $O(n^2)$ 。

考慮如何去優化這個事情。我們必須新增條件使得在 $\min\{A_i,B_j\} = \min\{A_j,B_i\}$ 時合法(該式子滿足傳遞性)。 由於 $cmp$ 函式的設計必須與 $i,j$ 對稱所以猜一猜?

可以通過花費的時間寫程式驗證是否合法,即驗證傳遞性與不可比性的傳遞性。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<climits>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define
fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int A[4],B[4];bool ff=1; bool cmp(int id1,int id2){return min(A[id1],B[id2])==min(A[id2],B[id1])?A[id1]<A[id2]:min(A[id1],B[id2])<min(A[id2],B[id1]);} int main(){ for(int a1=0;a1<=10;a1++) for(int b1=0;b1<=10;b1++) for(int a2=0;a2<=10;a2++) for(int b2=0;b2<=10;b2++) for(int a3=0;a3<=10;a3++) for(int b3=0;b3<=10;b3++){ A[1]=a1,A[2]=a2,A[3]=a3,B[1]=b1,B[2]=b2,B[3]=b3; if(cmp(1,2)&&cmp(2,3)&&!cmp(1,3)) ff=0,printf("WrongAnswer (%d,%d) (%d,%d) (%d,%d)\n",a1,b1,a2,b2,a3,b3); else if((!cmp(1,2)&&!cmp(2,1))&&(!cmp(2,3)&&!cmp(3,2))&&(cmp(1,3)||cmp(3,1))) ff=0,printf("WrongAnswer (%d,%d) (%d,%d) (%d,%d)\n",a1,b1,a2,b2,a3,b3); } if(ff) printf("Accepted\n"); return 0; }
View Code

使用時改變 $cmp$ 函式並提高值域即可?若出現 $Accepted$ 即為滿足嚴格弱序,可以直接使用。可以通過上述程式碼構造反例。

故可以按照通過上述程式碼的 $cmp$ 構造,時間複雜度 $O(n\log n)$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<climits>
#define int long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=1e6+11;
int N,A[MAXN],B[MAXN],C[MAXN],p[MAXN],sum,Maxn;
bool cmp(int id1,int id2){return min(A[id1],B[id2])==min(A[id2],B[id1])?A[id1]<A[id2]:min(A[id1],B[id2])<min(A[id2],B[id1]);}
signed main(){
    int cas=read();
    while(cas--){
        N=read(); for(int i=1;i<=N;i++) A[i]=read(),B[i]=read(),p[i]=i; sort(p+1,p+N+1,cmp);sum=0;
        for(int i=1;i<=N;i++) sum+=A[p[i]],C[i]=max(C[i-1],sum)+B[p[i]];
        printf("%lld\n",C[N]);
    }return 0;
}
View Code