1. 程式人生 > 其它 >Codeforces Round #696 (Div. 2) C. Array Destruction

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})

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;
}