密码(zhx原创题)

题面有所改动。(如有需要原题面或是测试数据的,请联系我:1979661141(QQ)
时限:1s 空间限制:256M
【问题描述】
在每个男生的心中都有一个女神,他们都渴望着和女神在一起的日子。
lovechq也不例外。这天女神拿着一道题来问lovechq,题目是:有N个数组成的数列,现不给出这N个数,而给出N
(N-1)/2个数,他们是原数列N个数的两两之和,要求用给出的数求出原数列中的N个数。
这下可把lovechq难倒了,于是他装模做样地对女神说题目比较难,他需要思考一宿,然后就偷偷跑来问你了,请你帮他解决问题。
【输入格式】
一行一个整数N。
接下来一行N×(N−1)/2个数,代表两两之和。
【输出格式】
第一行一个整数?代表解的个数。
接下来?行,每行N个数代表一组解,数从小到大排列。解的顺序按照字典序
从大到小排列。
【样例输入 1】
4
3 5 4 7 6 5
【样例输出 1】
1
1 2 3 4
【样例输入 2】
4
11 17 21 12 20 15
【样例输出 2】
2
4 7 8 13
3 8 9 12
【数据范围与规定】
对于30%的数据,1<=N<=5,N个数均不超过10。
对于60%的数据,1 ≤ N≤ 50,N个数均不超过100。
对于100%的数据,1 ≤ N ≤ 300,N个数均不超过108

先引用zhx原版题解:

令a1,a2,……,an为一组答案,那么给出的n*(n-1)/2个数中,最小的一定等于a1+a2,次小的一定等于a1+a3,为了确定a1 a2 a3我们还需要a2+a3的值。由于a1+a4可能小于a2+a3,所以我们不能确定a2+a3的值,那么由于数据范围挺小的,枚举a2+a3是n*(n-1)/2中的哪一个就好了。确定了a2+a3之后我们发现a1 a2 a3的值就都有了,然后a1+a4一定是剩下中最小的,依次类推一个一个解出剩下的数就搞定了。
难度评估:第2题难度

所以其实就是搜索,但是显然要剪枝。
首先计算出前三项,接下来枚举其他数和第一个数的和,然后判断是否冲突,中途各种判断,最后,如果没有问题,就保存结果。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,tol;
int num[50000];
int res[300];
int ans[300][300];
int vis[50000];
int cnt;
void check(int p){
	memset(vis,0,sizeof(vis));
	if((num[0]+num[1]+num[p])%2==1){
		return;
	}
	res[0]=(num[0]+num[1]+num[p])/2-num[p];
	res[1]=num[0]-res[0];
	res[2]=num[1]-res[0];
	vis[0]=vis[1]=vis[p]=1;
	for(int a=3,b=2;a<n;a++){
		while(b<tol&&vis[b]){
			b++;
		}
		if(b>=tol){
			return;
		}
		res[a]=num[b]-res[0];
		vis[b]=1;
		for(int c=1;c<a;c++){
			if(res[c]>res[a]){
				return;
			}
			int v=res[a]+res[c];
			int p=lower_bound(num,num+tol,v)-num;
			if(num[p]!=v){
				return;
			}
			int px=p;
			while(px>=0&&num[px]==num[p]){
				px--;
			}
			px++;
			while(px<tol&&vis[px]&&num[px]==num[p]){
				px++;
			}
			if(px>=tol||num[px]!=num[p]||vis[px]){
				return;
			}
			p=px;
			vis[p]=1;
		}
	}
	cnt++;
	for(int i=0;i<=n;i++){
		ans[cnt][i]=res[i];
	}
}
int main(){
	freopen("city.in","r",stdin);
	freopen("city.out","w",stdout);
	scanf("%d",&n);
	tol=n*(n-1)/2;
	for(int i=0;i<tol;i++){
		scanf("%d",&num[i]);
	}
	sort(num,num+tol);
	cnt=0;
	for(int a=2;a<tol;){
		check(a);
		int b=a;
		while(b<tol&&num[b]==num[a]){
			b++;
		}
		a=b;
	}
	printf("%d\n",cnt);
	for(int i=1;i<=cnt;i++){
		for(int j=0;j<n;j++){
			printf("%d",ans[i][j]);
			if(j!=n-1){
				printf(" ");
			}
		}
		printf("\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值