[ABC254Ex] Multiply or Divide by 2 题解

发布时间 2023-08-22 15:44:41作者: Ariasaka

前言

传送门

仅次于文本编辑器的紫水题(草

题意

给你两个集合 \(A\)\(B\) ,你可以把集合 \(A\) 的任意一项变为原来的 $\left \lfloor\frac{1}{2}\right \rfloor $ 或 \(2\) 倍,求至少需要操作几步才能使 \(A=B\)。如果无法将 \(A\) 变为 \(B\),输出 -1

解析

虽然今天学了字典树,但是没想到字典树做法。

考虑贪心,可以将“把 \(A\) 的任意元素变为原来的两倍”转换为“把 \(B\) 的任意元素变为原来的 \(\frac{1}{2}\)”,这两个方法是等效的。

我们维护两个优先队列 ab代表集合 \(A\)\(B\) ,每次取出两个集合的最大值进行转化,然后我们采用这样的贪心策略:

如果 a.top()==b.top() ,那么直接将这两个元素出队,这说明两个集合的这两个元素已经相等了。

如果 b.top()>a.top() ,那么将 b.top() 出队,除以2后再入队,这等价于将 集合 \(A\) 的最大值乘上2。

大家应该意识到了一个问题,如果将 b.top() 除以2的话,实际上是和 a.top()*2 的效果并不完全一样,因为除以2时会向下取整,所以到后面会一直多一个 1 而无法将 \(A\) 变为 \(B\),这种情况就需要输出 -1 了。

接下来如果 a.top()>b.top() 的话,跟上面一样,将 a.top() 出队,除以2后再入队即可,不需要考虑其他条件,因为在题意中已经说到需要向下取整了。

等等一个贪心为什么考试就我A了啊qwq

代码

那么代码就很简单了,使用优先队列维护即可,单次修改(除 a.top()==b.top() 时)执行 cnt++,最后输出 cnt 作为答案即可。

注释版

#include <bits/stdc++.h>
using namespace std;
int n,t,cnt; //cnt作为答案 
priority_queue<int> a,b; //优先队列 
int main(){
    ios::sync_with_stdio(0);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>t,a.push(t); //直接在a和b中入队,不需要静态数组储存,后面用不到静态数组的 
    for(int i=1;i<=n;i++)
        cin>>t,b.push(t);
    while(!a.empty()){
        if(a.top()==b.top()) //贪心策略1,见TJ 
            a.pop(),b.pop();
        else if(a.top()>b.top()){ //贪心策略3,见TJ 
            t=a.top();
			a.pop(),a.push(t>>1),cnt++;
        }
        else if(b.top()>a.top()){ //贪心策略2,见TJ 
            if(b.top()&1) //b.top()是奇数则不满足*2的条件 
                cout<<"-1",exit(0); //退出程序,exit(0)等效于在main中return 0;,但是不用花括号 
            t=b.top(); //需要一个变量临时保存当前的堆顶 
			b.pop(),b.push(t>>1),cnt++; //位运算卡常 x>>1==x/2 
        }
    }
    cout<<cnt;
    return 0; //好习惯 
}

无注释版

可以CTJ的版本

#include <bits/stdc++.h>
using namespace std;
int n,t,cnt;
priority_queue<int> a,b;
int main(){
    ios::sync_with_stdio(0);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>t,a.push(t);
    for(int i=1;i<=n;i++)
        cin>>t,b.push(t);
    while(!a.empty()){
        if(a.top()==b.top())
            a.pop(),b.pop();
        else if(a.top()>b.top()){
            t=a.top();
			a.pop(),a.push(t>>1),cnt++;
        }
        else if(b.top()>a.top()){
            if(b.top()&1)
                cout<<"-1",exit(0);
            t=b.top();
			b.pop(),b.push(t>>1),cnt++;
        }
    }
    cout<<cnt;
    return 0;
}

multiset做法

我这里提供一种 multiset 的做法,闲的没事写的(

#include <bits/stdc++.h>
using namespace std;
int n,t,cnt;
multiset<int> a,b;
int main(){
    ios::sync_with_stdio(0);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>t,a.emplace(t); //emplace比insert稍微快一点
    for(int i=1;i<=n;i++)
        cin>>t,b.emplace(t);
    while(!a.empty()){
        if(*a.rbegin()==*b.rbegin())
            a.erase(--a.end()),b.erase(--b.end());
        else if(*a.rbegin()>*b.rbegin()){
            t=*a.rbegin();
            a.erase(--a.end()),a.emplace(t>>1),cnt++;
        }
        else if(*a.rbegin()<*b.rbegin()){
            if((*b.rbegin())&1)
                cout<<"-1",exit(0);
            t=*b.rbegin();
            b.erase(--b.end()),b.emplace(t>>1),cnt++;
        }
    }
    cout<<cnt;
    return 0;
}

麻烦各位大犇们点个赞嘛qwq