LuoguP1286 两数之和

题面概括

将n个数两两相加得到n*(n-1)/2个和,给出这些和,求所有原数方案
n<=500
LuoguP1286

题解

此题原题是 n<10, 没啥可做的
先将 (n*(n-1)/2) 个数排序
设b[i]表示给定的数中第i小的, a[i]为原数第i小的
显然有: b[1]=a[1]+a[2],b[2]=a[1]+a[3]
那么 i>=3 时呢?
则是不一定的
考虑为什么不一定: 我们无法判断 (b[3] = a[1]+a[4]) 还是 (b[4] = a[2]+a[3])
那b[3]还可不可能等于其他的组合呢?
显然是不能的(不解释),
b[3]如此,b[4]呢? b[5]呢?

不难发现若 b[j]=a[p]+a[q] 当j确定后 (q_max=j+1),此时 p=1
所以我们可以通过枚举p,q(p!=1)来将b[j]删掉,剩下的数就一定是 a[1]+a[j+1]=b[j]
枚举a[1]则可以轻松推出剩下的数

删除操作用 multiset 实现,格外好写

代码

#include<bits/stdc++.h>
using namespace std;
#define int ll
#define re register
#define ll long long
#define get getchar()
#define in inline
in int read()
{
	int t=0; char ch=get;
	while(ch<'0' || ch>'9') ch=get;
	while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
	return t;
}
const int _=601;
multiset<int > s;
int n,b[_<<10],a[_],f[_],N,st,ans[_][_],tot;
in void solve(int k)
{
	a[1]=(b[2]+b[1]-b[k])/2;
	a[2]=b[1]-a[1];
	a[3]=b[k]-a[2];
}
void calc(int qwe)
{
	for(re int i=4;i<=n;i++)
	{
		multiset<int> :: iterator it=s.begin();
		a[i]=*it-a[1];
		s.erase(it);
		if(a[i]<a[i-1]) return;
		for(re int j=2;j<i;j++)
		{
			it=s.find(a[i]+a[j]);
			if(*it!=a[i]+a[j]) return;
			s.erase(it);
		}
	}
	tot++;
	for(re int i=1;i<=n;i++) { ans[tot][i]=a[i]; }
	f[qwe]=1;
}
signed main()
{
	while(cin>>n)
	{
		memset(f,0,sizeof(f));
		memset(ans,0,sizeof(ans));
		s.clear();
		tot=0;
		N=(n-1)*n/2;
		for(re int i=1;i<=N;i++) b[i]=read();
		sort(b+1,b+N+1);
		for(re int i=3;i<=N && b[2]+b[1]>=b[i];i++)
		{
			if((b[2]+b[1]-b[i])&1)  continue;
			if(b[i]==b[i-1]&&f[i-1]) {f[i]=1;continue;}
			solve(i);
			if(a[3]<a[2] || a[2]<a[1]) continue;
			s.clear();
			for(re int j=3;j<=N;j++)
				if(j!=i) s.insert(b[j]);
			calc(i);
		}
		if(tot==0) {
			cout<<"Impossible"<<endl;
			continue;
		}
		for(re int i=tot;i>=1;i--){
			for(re int j=1;j<=n;j++) cout<<ans[i][j]<<' ';
			cout<<endl;
		}
	}
	return 0;
}

原文地址:https://www.cnblogs.com/yzhx/p/13830524.html