ZOJ4043 : Virtual Singers

本文介绍了一种基于排序的匹配算法,适用于将两类元素$A$和$B$进行配对,通过巧妙的数据结构管理和反悔操作,实现了高效的匹配过程。算法采用优先队列管理未匹配的元素,确保每次匹配成本最小,同时支持未来的反悔操作,优化了整体匹配成本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

将所有$A$和$B$混在一起排序,那么每个$B$要匹配一个$A$,从左往右依次考虑每个数:

如果是一个$B$:

  • 如果左边没有多余的$A$,那么将其放入堆$q_C$中,表示这个$B$还未匹配。
  • 否则选择左边代价最小的$A$和这个$B$进行匹配,并把代价取反,加上这个$B$往右匹配的贡献后放入堆$q_B$中,表示未来某个$A$抢走这个$B$的代价,即费用流的反悔操作。

如果是一个$A$:

  • 如果$q_C$非空,那么将该$A$直接与$q_C$中代价最小的$B$进行匹配即可。
  • 否则如果$q_B$非空且抢走之前的某个$B$更优,那么抢走之前的$B$,并把这个代价取反,加上这个$A$往右匹配的贡献后放入堆$q_A$中,同理表示费用流的反悔操作。
  • 否则说明这个$A$暂时不需要和左边某个$B$进行匹配,将其放入待命区,也就是堆$q_A$中即可。

显然每个数只会进行$O(1)$次堆操作,故时间复杂度为$O((n+m)\log (n+m))$。

 

#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
int Case,n,m,i,ce,x;ll ans;
struct E{int x,y;E(){}E(int _x,int _y){x=_x,y=_y;}}e[200010];
inline bool cmp(const E&a,const E&b){return a.x<b.x;}
int main(){
  scanf("%d",&Case);
  while(Case--){
    priority_queue<ll,vector<ll>,greater<ll> >A,B,C;
    scanf("%d%d",&n,&m);
    ce=0;
    while(n--){
      scanf("%d",&x);
      e[++ce]=E(x,0);
    }
    while(m--){
      scanf("%d",&x);
      e[++ce]=E(x,1);
    }
    sort(e+1,e+ce+1,cmp);
    ans=0;
    for(i=1;i<=ce;i++){
      ll x=e[i].x;
      if(e[i].y==0){
        if(!C.empty()){
          ll t=C.top();
          C.pop();
          ans+=x+t;
        }else if(!B.empty()){
          ll t=B.top();
          if(t+x<0){
            B.pop();
            ans+=x+t;
            A.push(-t-x-x);
          }else A.push(-x);
        }else A.push(-x);
      }else{
        if(!A.empty()){
          ll t=A.top();
          A.pop();
          ans+=x+t;
          B.push(-t-x-x);
        }else C.push(-x);
      }
    }
    printf("%lld\n",ans);
  }
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值