ZROI 18D 杜老师的拷问

Description

dls 在心中生成了 n n 个正整数,给出了两两数字的和共 n × ( n 1 ) 2 \frac{n \times (n-1)} {2} 个,求所有可能的方案。

1 n 300 , 1 a i + a j 2 × 1 0 8 1 \leq n \leq 300, 1 \leq a_i + a_j \leq 2 \times 10^8

Solution

构造。令 n n 个数从小到大为 a 1 , a 2 , . . , a n a_1,a_2,..,a_n

用 set 维护所有的和, 最小的和一定是 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 + 1 n + 1 个位置枚举,因为只可能 a 1 + a i a_1 + a _ i 比它小,然后能解方程求出 a 1 , a 2 , a 3 a_1,a_2,a_3 ,把这个 3 3 个数删掉,剩下最小的数一定为 a 1 + a 4 a_1 + a_4 ,以此类推可以把所有的数都求出来。

在枚举中判断 a 2 + a 3 a_2 + a_3 是否合法,只要判断和是否存在,而且这个和还有没被用的。所以用掉一个和让和减少一个。

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; 
}
发布了28 篇原创文章 · 获赞 38 · 访问量 487

猜你喜欢

转载自blog.csdn.net/qq_39984146/article/details/104225710