leetcode 2561. 重排水果 困难

你有两个果篮,每个果篮中有 n 个水果。给你两个下标从 0 开始的整数数组 basket1 和 basket2 ,用以表示两个果篮中每个水果的交换成本。你想要让两个果篮相等。为此,可以根据需要多次执行下述操作:

  • 选中两个下标 i 和 j ,并交换 basket1 中的第 i 个水果和 basket2 中的第 j 个水果。
  • 交换的成本是 min(basket1i,basket2j) 。

根据果篮中水果的成本进行排序,如果排序后结果完全相同,则认为两个果篮相等。

返回使两个果篮相等的最小交换成本,如果无法使两个果篮相等,则返回 -1 

示例 1:

输入:basket1 = [4,2,2,2], basket2 = [1,4,1,2]
输出:1
解释:交换 basket1 中下标为 1 的水果和 basket2 中下标为 0 的水果,交换的成本为 1 。此时,basket1 = [4,1,2,2] 且 basket2 = [2,4,1,2] 。重排两个数组,发现二者相等。

示例 2:

输入:basket1 = [2,3,4,1], basket2 = [3,2,5,1]
输出:-1
解释:可以证明无法使两个果篮相等。

提示:

  • basket1.length == bakste2.length
  • 1 <= basket1.length <= 10^5
  • 1 <= basket1i,basket2i <= 10^9

分析:先用哈希表分别记录两个果篮里出现过的水果成本出现次数。想让交换成本尽可能小,有两种方法,一种是用小成本的水果与大成本的水果进行交换;另一种方法是用成本最小的水果,交换两次,举例如下:

[3,4,4] 和 [3,5,5],可以 4 和 5 直接交换,也可以用 3 先交换 5,再用 3 交换 4.

能否使两个果篮相等,取决于所有出现的水果交换成本是否都是偶数。如果存在某个成本出现过奇数次,则一定不可能相等。统计完所有的成本出现次数后,对于每一次交换,都分别检查两种交换结果的大小,取较小的方法进行统计,直到全部交换完。

class Solution {
public:
    long long minCost(vector<int>& basket1, vector<int>& basket2) {
        sort(basket1.begin(),basket1.end());
        sort(basket2.begin(),basket2.end());

        int t1=0,t2=0,t=0,mini=1000000001;
        map<int,int>cnt1,cnt2;
        int val1[100010]={0},val2[100010]={0};
        for(int i=0;i<basket1.size();++i)
        {
            cnt1[basket1[i]]++;mini=fmin(mini,basket1[i]);
            if(cnt1[basket1[i]]==1)val1[t1++]=basket1[i];
        }
        for(int i=0;i<basket2.size();++i)
        {
            cnt2[basket2[i]]++;mini=fmin(mini,basket2[i]);
            if(cnt2[basket2[i]]==1)val2[t2++]=basket2[i];
        }

        long long ans=0;
        //exval表示待交换的成本价格,exnum表示待交换的成本数量,下标一一对应
        long long exnum[200010]={0},exval[200010]={0};
        //下面的循环用于统计需要交换的成本
        for(int i=0,j=0;i<t1||j<t2;)
        {
            // printf("i=%d j=%d val1=%d val2=%d\n",i,j,val1[i],val2[j]);
            if(i<t1&&j<t2)
            {
                if(val1[i]!=val2[j])//不相等说明小的那个在另一个篮子里不存在,要分一半
                {
                    if(val1[i]<val2[j])
                    {
                        if(cnt1[val1[i]]&1)return -1;
                        exval[t]=val1[i],exnum[t]=cnt1[val1[i]]/2;t++;i++;
                    }
                    else
                    {
                        if(cnt2[val2[j]]&1)return -1;
                        exval[t]=val2[j],exnum[t]=cnt2[val2[j]]/2;t++;j++;
                    }
                }
                else if(val1[i]==val2[j])//相等则平均分
                {
                    if((cnt1[val1[i]]+cnt2[val2[j]])&1)return -1;

                    if(cnt1[val1[i]]==cnt2[val2[j]])i++,j++;
                    else exval[t]=val1[i],exnum[t]=(cnt1[val1[i]]+cnt2[val2[j]])/2-fmin(cnt1[val1[i]],cnt2[val2[j]]),t++,i++,j++;
                }
            }
            else if(i<t1)
            {
                while(i<t1)
                {
                    if(cnt1[val1[i]]&1)return -1;
                    exval[t]=val1[i],exnum[t]=cnt1[val1[i]]/2;t++;i++;
                }
            }
            else if(j<t2)
            {
                while(j<t2)
                {
                    if(cnt2[val2[j]]&1)return -1;
                    exval[t]=val2[j],exnum[t]=cnt2[val2[j]]/2;t++;j++;
                }
            }
            // printf("i=%d j=%d t1=%d t2=%d\n",i,j,t1,t2);
        }
        // printf("t=%d\n",t);
        // for(int i=0;i<t;++i)
        // {
        //     printf("val=%d num=%d\n",exval[i],exnum[i]);
        // }

        for(int i=0,j=t-1;i<=j;)//注意每次交换都要比较两种方法的代价
        {
            if(i==j)ans+=fmin(exval[i]*exnum[j]/2,mini*exnum[j]),exnum[i]-=exnum[j],j--;
            else if(exnum[i]>exnum[j])ans+=fmin(exval[i]*exnum[j],2*mini*exnum[j]),exnum[i]-=exnum[j],j--;
            else if(exnum[i]==exnum[j])ans+=fmin(exval[i]*exnum[j],2*mini*exnum[j]),j--,i++;
            else if(exnum[i]<exnum[j])ans+=fmin(exval[i]*exnum[i],2*mini*exnum[i]),exnum[j]-=exnum[i],i++;
        }

        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值