相邻交换法 & 皇后游戏

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

 

posted @ 2020-11-28 21:02  siruiyang_sry  阅读(154)  评论(0编辑  收藏  举报