Codeforces Round #696 (Div. 2) C. Array Destruction
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})
嚶 嚶 嚶)
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;
}