C Array Destruction
题目链接https://codeforces.com/contest/1474/problem/C
题意:
给出一个长度为2n的数组,判断数组内的任意三个数是否满足a + b = c,若满足,删除a,b;再判断数组内是否存在另外的两个数是否满足d + e = max(a, b),若满足,就删除d,e;一直循环做这个操作,直到数组内的数字被删除完毕为止并输出最初的c和之后两个数的组合即(a, b);(c, d)…(最初提到的c就是第一组a, b的和而已)
所以这个题就和明确了,每次找到当前数组的最大值,然后找到一个值与之匹配相加,如果其和等于上一个匹配的最大值,则继续匹配,一直匹配到数组所以的数字都被匹配为止。那没办法全部匹配的呢?那就是输出no的情况了。
很明显这就是个递归问题,但是递归开始是怎样的呢?第一个匹配是怎样找到的呢,回想题意,我们不难看出这就有些像是分蛋糕,每次都将当前最大的蛋糕分成大小不同的两份,然后扔掉小的,再分大的那一份,重复这个工作。所以不难想到第一个匹配的第一个值必然是数组的最大值,那第二个值是哪一个值呢,我们无法求出,所以都试试,看看能不能让后面剩余的值全部匹配就行了,所以我们需要枚举从1~2n - 1之间的值为第一个匹配的第二个值就好了。
那么之后怎么写呢?如果数组长度为2的话,那不论如何都是可以匹配的,因为c可以直接变成这两个数字的和,那比2长的话,第二个匹配的和肯定就是数组最大值,那按照分蛋糕的思想,第二个匹配的第一个值肯定就是当前未被匹配过的最大值咯,那第二个值呢?两个数的和与其中的一个数都知道了,另一个数不久减一下就好了嘛?那不就是判断这两个数的差是不是存在于数组就好了,等等,你说是不是想到了遍历数组?欧?这就
O
(
n
3
)
O(n^{3})
O(n3)的时间复杂度了(因为你任何一个匹配的第一个值都是需要用循环一个一个判断是否能找到其匹配值的,在这里就有
n
2
n^{2}
n2了,再加一个遍历的话就是
n
3
n^{3}
n3),你就会超时(比如我之前超时的代码:(嘤 嘤 嘤)
bool solve(int n, int num, int &cnt){//num为当前遍历的第一次匹配的第二个值在数组的位置
if(n == 2) return true;
sort(a + 1, a + n + 1);
fill(b + 1, b + n + 1, false);
b[n] = true;
b[num] = true;
int pre = a[n];
for(int i = n - 1; i > 0; i --){
if(b[i]) continue;
b[i] = true;
for(int j = 1; j < i; j ++){
if(b[j]) continue;
if(a[i] + a[j] == pre) {
ans[cnt][0] = a[i];
ans[cnt][1] = a[j];
b[j] = true;
pre = a[i];
cnt ++;
if(cnt == n / 2 - 1) return true;
break;
}
}
}
return false;
}
我们在看一下数组内数字的范围:1e6????这不直接散列???妈妈我会散列思想我又出息了,嘤 嘤 嘤。。(确实,都知道判断是否被匹配过了,还不用散列的思想,我当时太蠢了。)
好吧,直接将数组内的某数字的个数存在一个散列表里面就行了,被匹配了就减一个,减到只剩0了就是这个数再数组内全部被匹配过了,或者数组内根本没这个数。这样就直接将遍历的那一步省了,时间复杂度变成了
O
(
1
)
O(1)
O(1),所以在这里的时间复杂度为
O
(
n
2
)
O(n^{2})
O(n2), 就不会超时了。
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
int a[maxn];
int vis[maxn];
int ans[maxn][5];
bool solve(int n, int num, int &cnt){
if(n == 2) return true;
sort(a + 1, a + n + 1);
fill(vis, vis + maxn, 0);//初始化
for(int i = 1; i <= n; i ++){
vis[a[i]] ++;
}
vis[a[n]] --;
vis[a[num]] --;
int pre = a[n];
for(int i = n - 1; i > 0; i --){
if(!vis[a[i]]) continue;
vis[a[i]] --;
if(vis[pre - a[i]] > 0){
vis[pre - a[i]] --;
ans[cnt][0] = a[i];
ans[cnt][1] = pre - a[i];
cnt ++;
pre = max(a[i], pre - a[i]);
if(cnt == n / 2 - 1) return true;
}
else return false;
}
return false;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(); cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif//提交的时候可以不用注释
int t; cin >> t;
while(t--){
int n; cin >> n;
for(int i = 1; i <= 2 * n; i ++){
cin >> a[i];
}
int i = 1;
for(; i < 2 * n; i ++){
int cnt = 0;
if(solve(2 * n, i, cnt)){
cout << "YES\n" << a[i] + a[2 * n] << "\n";
cout << a[i] << " " << a[2 * n] << "\n";
for(int j = 0; j < cnt; j ++){
cout << ans[j][0] << " " << ans[j][1] << "\n";
}
break;
}
}
if(i == 2 * n) cout << "NO\n";
}
return 0;
}