C. Array Destruction

题目链接:C. Array Destruction
题意描述:
分解过程:例如14 分解为 11, 3后, 取max(11,3) 继续分解,分解为5,6, 取max(5,6)…
给定数组,求是否存在一个数x在分解过程中,将数组中所有数恰好都用上,若存在输出分解过程

思路:

  • 分解后的两个数一定是数组中可利用的的最大值和其中的某一个数 (贪心)
反证法:假设被分解的数不是可利用的最大的数,那么这个最大的数在之后的分解中必不能再被用到,
因为它太大了,将会大于欲分解的数 
  • 利用multiset 可方便的解决

代码:

/*
 巧妙地利用了本题的特点
 根据题意,最初被分解的数一定是最大值和其中某一个数的的和,
 当继续进行分解时这个数会被分解为 数组中可利用的的最大值和其中的某一个数 (贪心) 
 	反证法:假设不是被分解为最大的数,那么这个最大的数在之后的分解中必不能再被用到,
	因为它太大了,将会大于欲分解的数 
利用multiset 插入所有数 
*/ 
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>

using namespace std;

vector<int> check(int n, vector<int> a, int x)
{
    multiset<int> s;  //multiset:可插入多个值,并进行排序 logn
    for (auto e : a)
        s.insert(e);
    vector<int> res;
    for (int i = 0; i < n; i++)  //循环遍历n次 
    {
        auto it1 = s.end();
        it1--;
        int y = x - *it1;
        s.erase(it1);
        auto it2 = s.find(y);
        if (it2 == s.end())
            return {};  //返回一个空变长数组 
        res.push_back(x - y);
        res.push_back(y);
        x = max(x - y, y);
        s.erase(it2);
    }
    return res;
}

void solve()
{
    int n;
    cin >> n;
    vector<int> a(2 * n);
    for (int i = 0; i < 2 * n; i++)
        cin >> a[i];
    sort(a.begin(), a.end());
    for (int i = 0; i < 2 * n - 1; i++)
    {
        int x = a[i] + a[2 * n - 1];  //判断哪一个值和最大值相加构成最初的分解值 
        vector<int> res = check(n, a, x);  //这里直接返回路径 
        if (res.size())
        {
            cout << "YES\n";
            cout << x << "\n";
            for (int j = 0; j < n; j++)
            {
                cout << res[2 * j] << " " << res[2 * j + 1] << "\n";  //输出路径 
            }
            return;
        }
    }
    cout << "NO\n";
}

int main()
{
    int t;
    cin >> t;
    for (int c = 0; c < t; c++)
    {
        solve();
    }
}

手动模拟multiset 功能的代码

/*
	这样实际上就是对multiset的模拟 
*/ 
#include <bits/stdc++.h>

using namespace std;

const int A = 1e6 + 12;  

int cnt[A];  //一个标记数组,欲标记所有的数 
void reset(vector<int> a)
{
    for (int i = 0; i < a.size(); i++)
    {
        cnt[a[i]] = 0;
    }
}

void solve()
{
    int n;
    cin >> n;
    vector<int> a(2 * n);
    for (int i = 0; i < 2 * n; i++)
    {
        cin >> a[i];
    }
    sort(a.begin(), a.end());
    int t = 0;
    for (int i = 0; i < 2 * n - 1; i++)
    {
        for (int j = 0; j < 2 * n; j++)
            cnt[a[j]]++;  //记录每个元素出现的次数 
        int j = 2 * n - 1;
        int x = a[i] + a[j];
        vector<int> rm;
        for (int op = 0; op < n; op++)  //循环n次,找出2*n个数 
        {
            while (j > 0 && cnt[a[j]] == 0)  //查找当前数组中仍能利用的最大值 
                j--;  						 //实际上是在模拟multiset的功能 
            rm.push_back(a[j]);
            rm.push_back(x - a[j]);
            cnt[a[j]]--, cnt[x - a[j]]--;
            if (cnt[a[j]] < 0 || cnt[x - a[j]] < 0)
            {
                cnt[a[j]] = 0;
                cnt[x - a[j]] = 0;
                break;  
            }
            x = max(x - a[j], a[j]);
            if (op + 1 == n)
                t = 1;
        }
        reset(a);
        if (t)
        {
            cout << "YES\n";
            cout << rm[0] + rm[1] << "\n";
            for (int i = 0; i < rm.size(); i++)
            {
                cout << rm[i] << " \n"[i % 2];
            }
            return;
        }
    }
    cout << "NO\n";
    reset(a);
}

int main(int argc, char* argv[])
{
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
}

参考CF上的:题解

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值