相邻交换法 & 皇后游戏
填填初二时研究过的坑(今天初二考试题)。相邻交换法的应用。
跳过所有前面的简化式子,从 $\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; }
使用时根据猜测改变 $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; }