题目链接: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上的:题解