唐纳德先生与这真的是签到题吗(数学)

原题: https://acm.ecnu.edu.cn/contest/126/problem/C/

题意:

有一个n长数组,两两相加得到一个n(n-1)/2的数组,给出这个n(n-1)/2的数组(排序后),求出原数组(任意解)

解析:

假设原数组从小到大为 a 1 , a 2 , a 3 . . . a n a_1,a_2,a_3...a_n ,它们的和中,最小的一定是 a 1 + a 2 a_1+a_2 ,其次是 a 1 + a 3 a_1+a_3 ,但是 a 2 + a 3 a_2+a_3 排第几不知道。
但是,我们知道 a 2 + a 3 a_2+a_3 可以排到的最后面是第n位(前面是 a 1 + a 2 . . a 1 + a n a_1+a_2..a_1+a_n ),所以我们枚举 a 2 + a 3 a_2+a_3 的名次,那么就可以解出 a 1 , a 2 , a 3 a_1,a_2,a_3 分别使什么,我们将这三个数的两两之和从原数组中删除,那么此时最小的就是 a 1 + a 4 a_1+a_4 ,那么就得到了 a 4 a_4 ;再删除 a 4 a_4 和前面的数的和,以得到 a 5 a_5 ……

在求 a 1 , a 2 , a 3 a_1,a_2,a_3 的时候有个细节,就是不能除二会出错

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define debug(i) printf("# %d\n",i)

int a[50100];
multiset<int>S;
multiset<int>::iterator it;
int ans[301];

bool fin(int idx,int n){
    ans[1]=(a[1]+a[2]-a[idx])/2;
    ans[2]=(a[1]-a[2]+a[idx])/2;
    ans[3]=(-a[1]+a[2]+a[idx])/2;
    if(a[1]+a[2]-a[idx]!=ans[1]*2||a[1]-a[2]+a[idx]!=ans[2]*2||-a[1]+a[2]+a[idx]!=ans[3]*2)return 0;

    multiset<int>s(S);

    if(ans[1]<0||ans[2]<0||ans[3]<0)return 0;

    it=s.find(a[idx]);
    s.erase(it);
    for(int i=4;i<=n;i++){
        ans[i]=*s.begin()-ans[1];
        if(ans[i]<0)return 0;
        s.erase(s.begin());
        for(int j=2;j<i;j++){
            it=s.find(ans[j]+ans[i]);
            if(it==s.end())return 0;
            s.erase(it);
        }
    }
    return 1;
}

int main(){int t;scanf("%d",&t);while(t--){

    S.clear();
    int n;
    scanf("%d",&n);
    int nn=n*(n-1)/2;
    for(int i=1;i<=nn;i++){
        scanf("%d",a+i);
        if(i>2)S.insert(a[i]);
    }

    for(int i=3;i<=n;i++){
        if(fin(i,n))break;
    }

    for(int i=1;i<=n;i++){
        printf("%d%c",ans[i],(i==n?'\n':' '));
    }

}}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/86562556