皇后遊戲
填填初二時研究過的坑(今天初二考試題)。相鄰交換法的應用。
跳過所有前面的簡化式子,從 $\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 #defineView Codefi 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; }
使用時改變 $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