分析(来自黑书):
为了研究方便,设这n个整数从小到大依次为A1,A2,A3,…,也将n(n-1)/2个和数从小到大依次设为K1,K2,K3…
从边缘数据入手,根据简单的大小比较,很容易发现A1+A2=K1,A1+K3=K2。因为无法直接确定A2+A3的大小
(因为K3有可能等于A1+A4或A2+A3),所以只能假设A2+A3 = Kx,然后通过解方程求出A1,A2,A3的值。知道A1,
A2,A3以后,接下来的工作就是依次递推出每个数。假设已经求出前w个数的值,并将相应的w(w-1)/2个和数从数
列{Kn}中去除,那么考虑剩下的和数中最小的那一个Ki。为使得Ki最小,Ki必然是A1Aw中的最小数与Aw+1An
中的最小数相加而得,即Ki=A1+Aw+1。由于Ki,A1已知,因此Aw+1也确定了。
下面举个例子。
K={4,5,7,10,12,13,13,14,19},因为A1+A2总是最小的,A1+A3是第二小的。所以:
A1+A2=4(K1)
A1+A3=5(K2)
我们不知道A2+A3的值,但是由于只有A1+Ax(1<=x<=n)可能比它小,因此它在K中的位置应该为3~(n+1),
。我们枚举每种情况,
假设A2+A3=K3=7得到方程组:
A1+A2=4(K1)
A1+A3=5(K2)
A2 +A3=7 (K3 )
它有整数解A1=1,A2=3,A3=4。把这三个数两两之和K1,K2,K3去掉,则现在K={10,11,12,13,13,14,19}。这三个数中最小的是10,显然它等于A1+A4,因此A4=10-A1=9。把A4产生的和A1+A4,A2+A4,A3+A4去掉,得:
K={11,13,14,19}。其中最小数11应该等于A1+A5,因此A5=11-A1=10。
假设A2+A3=K4=10,方程组没有整数解;
假设A2+A3=K5=11,方程组没有整正整数解。
因此本题的唯一解是A={1,3,4,9,10}。枚举x的值(A2+A3=Kx)需要O(n)次,每次枚举需要递推O(n)个
————————————————
版权声明:本文为CSDN博主「Raise」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/linraise/article/details/17243567
一开始写的时候没有检查在选出来的那个最小的和数所对应的A3的解的情况下,每次求出一个数,该数是否和前面的数相加都符合要求。导致错了。实际上根据分析,当选出了w个数,就要从k数组中删去所有这w个数的任意两个数的和。必定是要检查的。然后又写错了一个数,死活检查不出来,浪费了好多时间。
对于这种繁琐,琐碎的代码自己还有很大的提升空间,需要先把框架搭好,想清楚每步应该怎么做来节省时间。
#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
int n, t;
int ans_num[50 + 1];
int k[1225+1];
int vis[1225 + 1];
int A1;
bool isRebuild() //判断是否可以重建数表
{
//printf("111\n");
int j = 3, i, q, p;
for (i = 4; i <= n; ++i)
{
//printf("最初j=%d\n", j);
while (j <= t && vis[j]==1)
j++;
//printf("最小和数下标j=%d\n", j);
if (j > t)//所有都已经算出来,返回真
return true;
vis[j] = true;
ans_num[i] = k[j] - A1; // 找未被访问过的第一个值
//printf("ans_num[%d]=%d\n", i, ans_num[i]);
//检查
for (q = 2; q < i; ++q)
{
for (p = j + 1; p <= t; ++p)
{
if (vis[p]==0 && ans_num[q] + ans_num[i] == k[p])
{
//printf("ans_num[%d]+ans_num[%d]=%d\n", q, i, k[p]);
vis[p] = 1;
//printf("vis[%d]=%d\n", p, vis[p]);
break;
}
}
if (p > t) //不符合要求
return false;
}
//printf("符合要求\n");
}
return true;
}
int main()
{
while (scanf("%d", &n) != EOF)
{
t = n * (n - 1) / 2;
for (int i = 1; i <= t; i++)
{
scanf("%d", k + i);
}
sort(k + 1, k + t + 1);
//printf("111\n");
/*
printf("排序后的数组为\n");
for (int i = 1; i <= t; i++)
{
printf("%d ", k[i]);
}
printf("\n");
*/
for (int x = 3; x <= n; x++) //A2+A3=Kx,而只有A1+At的形式有可能比A2+A3小,所以A2+A3最大的时候排在第n位,所以枚举到第n位
{
//printf("x=%d\n", x);
//int vis_num = 3;
double temp = (k[1] + k[2] - k[x]) / 2;
A1 = (int)temp;
if ((temp - (double)A1) == 0)
{
//printf("x=%d\n",x);
ans_num[1] = A1;
ans_num[2] = k[1] - A1;
ans_num[3] = k[2] - A1;
memset(vis, 0, sizeof(vis));
vis[x] = 1;
if (isRebuild())
{
//printf("111\n");
for (int i = 1; i <= n; i++)
{
printf("%d\n", ans_num[i]);
}
break;
}
}
}
}
return 0;
}