Description
dls 在心中生成了 个正整数,给出了两两数字的和共 个,求所有可能的方案。
Solution
构造。令 个数从小到大为 。
用 set 维护所有的和, 最小的和一定是 ,次小的一定是 ,但是不知道 是哪一个。所有枚举 这个数是哪一个。只在前 个位置枚举,因为只可能 比它小,然后能解方程求出 ,把这个 个数删掉,剩下最小的数一定为 ,以此类推可以把所有的数都求出来。
在枚举中判断 是否合法,只要判断和是否存在,而且这个和还有没被用的。所以用掉一个和让和减少一个。
Code
#include <bits/stdc++.h>
using namespace std;
map<int, int> all;
const int N = 500 + 5;
int a[N * N], ans[N][N * N], tot = 0, n, m;
bool add(int i, int x){
ans[tot][i] = x;
for (int j = 1; j <= i - 1; j++)
if (all[x + ans[tot][j]] == 0) return 1; // 生成的数与其他的数的和不存在
else if (--all[x + ans[tot][j]] == 0) all.erase(x + ans[tot][j]); // 和减少一个
return 0;
}
void solve(int a12, int a13, int a23){
if ((a12 + a13 + a23) % 2 == 1) return; // 如果分出的数不能被整除就不合法
all.clear();
for (int i = 1; i <= m; i++) all[a[i]]++;
add(1, (a12 + a13 - a23) / 2); // a1 + a2 + a1 + a3 - a2 - a3 = 2 * a1 2 * a1 / 2 = 1
add(2, (a12 + a23 - a13) / 2); // a2
add(3, (a13 + a23 - a12) / 2); // a3
for (int i = 4; i <= n; i++) if (add(i, all.begin() -> first - ans[tot][1])) return ;
tot++;
}
int main(){
cin >> n; m = n * (n - 1) / 2;
for (int i = 1; i <= m; i++) cin >> a[i]; // 读入
sort(a + 1, a + m + 1); // 排序
for (int i = 1; i <= n + 2; i++)
if (a[i] != a[i - 1]) solve(a[1], a[2], a[i]);
cout << tot << endl; // tot : 答案的可能数
for (int i = 0; i < tot; i++) for (int j = 1; j <= n; j++) printf("%d%c", ans[i][j], j == n ? '\n' : ' ');
return 0;
}