ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

UVa 10332 The Absent Minded Professor

UVa 10332 The Absent Minded Professor 题目描述给定NNN3≤N≤503 \le N \le 503≤N≤50个金属球它们的实际重量未知。教授用天平测量了所有两两球之间的重量差取绝对值即大减小得到(N∗(N−1))/2(N*(N-1))/2(N∗(N−1))/2个正整数读数小于100001000010000。现在需要为每个球分配一个非负整数重量使得这些重量两两差值的多重集合恰好等于给定的读数集合。若存在多解则要求输出按升序排列的重量序列并规定第一个重量最小重量必须为000且若存在镜像解即把每个重量www替换为M−wM - wM−w其中MMM为最大重量则优先输出字典序较小的那个即优先尝试较大的中间点。若无解输出Incorrect Balance.。输入包含多组测试数据每组以NNN开头随后一行或多行包含C(N,2)C(N,2)C(N,2)个正整数。输入以EOF\texttt{EOF}EOF结束。输入格式每组测试数据第一行是一个整数NNN3≤N≤503 \le N \le 503≤N≤50。接下来在若干行内给出(N∗(N−1))/2(N*(N-1))/2(N∗(N−1))/2个正整数小于100001000010000表示所有两两重量差的读数。输入可能跨多行但总数固定。输出格式对于每组数据若存在解则在一行中输出NNN个整数按重量升序排列且最小重量为000。若不存在则输出一行Incorrect Balance.。样例输入3 1 2 3 3 1 2 2 6 1 2 2 2 3 3 3 4 5 5 5 6 7 8 10输出0 2 3 Incorrect Balance. 0 3 5 6 8 10题目分析本题本质上是一个一维点集重建问题Turnpike Problem\texttt{Turnpike Problem}Turnpike Problem给定nnn个点之间的所有两两距离即距离多重集合要求恢复出这些点在直线上的位置坐标。已知最小坐标为000且所有坐标均为非负整数。由于距离集合只给出所有无序点对的距离而点与点之间的相对位置未给出因此需要从距离信息中反推出点的坐标。经典解法是回溯搜索递归 剪枝。因为N≤50N \le 50N≤50距离值不超过100001000010000但组合数C(50,2)1225C(50,2)1225C(50,2)1225搜索空间理论上很大但实际可行的剪枝策略能保证在题目限制内高效运行。关键观察最大距离max⁡D\max DmaxD必定等于最大坐标与最小坐标000之差因此可以直接确定两个端点000和Mmax⁡DM \max DMmaxD。剩余待确定的点都在(0,M)(0, M)(0,M)之间。每次从剩余距离集合中取出当前最大值ddd它必然对应某个尚未确定的点与已确定点集中的某个点之间的距离。由于已知已有点集这个新点只可能有两种候选位置距离000为ddd即坐标为ddd距离MMM为ddd即坐标为M−dM - dM−d。依次尝试这两种候选位置并检查该候选点与所有已有点的距离是否均在剩余距离集合中出现且数量足够。若成功则删除这些距离将该点加入已有点集继续递归。若失败则回溯。这种回溯策略在多数情况下能快速找到解或无解。解题思路数据结构与预处理将所有读数存入数组diffs并升序排序。将数组转换为多重集合std::multisetint以便支持快速查找和删除单个元素。确定最大距离maxVal diffs.back()固定初始点集为{0, maxVal}并从多重集合中删除maxVal这一项因为对应这对端点的距离已被使用。回溯函数设计定义bool dfs(multisetint ms, vectorint points)其中ms为当前剩余的未匹配距离的多重集合points为当前已确定的坐标集合升序未要求但内部可无序。递归终止条件当points.size() N时若ms为空则找到完整解否则失败。递归过程若ms为空但点数未满则返回false。取ms中的最大值d *ms.rbegin()。候选1令x d。若0 x maxVal且x不在points中则检查该候选点与所有已有点的距离是否都能在ms中找到足够的数量。具体做法统计所需差值abs(x - p)的频次need然后检查ms中每个差值是否至少达到该频次。若满足则从ms中删除这些差值将x加入points递归调用dfs。若递归成功则返回true否则恢复删除的差值并移除x。候选2令x maxVal - d。若x与候选1不同且0 x maxVal且x不在points中则进行同样的检查与递归。若两种候选均失败返回false。正确性说明最大距离maxVal必对应端点0和maxVal这一确定是必然的因为任意两点距离不可能超过maxVal且maxVal只能由最小和最大点产生。每次取剩余距离中的最大值d在已有点集确定的情况下该距离必定来自某个未确定点与某个已有点的距离。由于已有点集包含端点0和maxVal未确定点只能位于(0,maxVal)(0, maxVal)(0,maxVal)内因此它到0的距离为d或到maxVal的距离为d这两种情况恰好对应坐标d和maxVal - d。若都不成立则无解。递归枚举所有可能性且通过数量检查保证不会错误地匹配距离因此算法是完备的。镜像解与输出顺序题目要求输出“第一个”解且规定最小重量为000。镜像解是指将每个重量www替换为M−wM - wM−wMMM为最大重量此时距离集合不变。为了输出字典序较小的那个解我们在回溯时优先尝试候选点x d即较大的中间点因为这样得到的点集更倾向于偏大其镜像则偏小由于我们固定了最小点为000优先尝试大的点会使最终序列的第二个元素较大而镜像的第二个元素较小所以优先尝试大的点会输出字典序较小的解。复杂度分析最坏情况下递归深度为N−2N-2N−2除去两个端点每次尝试两个分支故搜索树规模为O(2N)O(2^{N})O(2N)但实际剪枝数量检查会大幅减少。由于N≤50N \le 50N≤50理论上2502^{50}250不可接受但实际该问题的回溯在随机数据上表现良好且NNN较小时可接受NNN较大时因距离值有限且重复多剪枝更有效。每次尝试需检查当前点与所有已有点的距离复杂度O(∣points∣)O(|points|)O(∣points∣)并操作multiset的查找和删除单次检查复杂度O(∣points∣⋅log⁡C)O(|points| \cdot \log C)O(∣points∣⋅logC)其中CCC为剩余距离个数。整体时间复杂度依赖于实际数据但在N≤50N \le 50N≤50且距离值范围较小的条件下可通过本题。代码实现// The Absent Minded Professor// UVa ID: 10332// Verdict: Accepted// Submission Date: 2026-06-24// UVa Run Time: 0.010s//// 版权所有C2026邱秋。metaphysis # yeah dot net#includebits/stdc.husingnamespacestd;intN,maxVal;boolinPoints(constvectorintpoints,intx){for(intp:points)if(px)returntrue;returnfalse;}booldfs(multisetintms,vectorintpoints){if((int)points.size()N)returnms.empty();if(ms.empty())returnfalse;intd*ms.rbegin();// 当前最大剩余差值// 候选点1x d与0的距离为dif(d0dmaxVal!inPoints(points,d)){mapint,intneed;for(intp:points)need[abs(d-p)];booloktrue;for(autokv:need)if((int)ms.count(kv.first)kv.second){okfalse;break;}if(ok){for(autokv:need)for(inti0;ikv.second;i)ms.erase(ms.find(kv.first));points.push_back(d);if(dfs(ms,points))returntrue;points.pop_back();for(autokv:need)for(inti0;ikv.second;i)ms.insert(kv.first);}}// 候选点2x maxVal - d与maxVal的距离为dintx2maxVal-d;if(x2!dx20x2maxVal!inPoints(points,x2)){mapint,intneed;for(intp:points)need[abs(x2-p)];booloktrue;for(autokv:need)if((int)ms.count(kv.first)kv.second){okfalse;break;}if(ok){for(autokv:need)for(inti0;ikv.second;i)ms.erase(ms.find(kv.first));points.push_back(x2);if(dfs(ms,points))returntrue;points.pop_back();for(autokv:need)for(inti0;ikv.second;i)ms.insert(kv.first);}}returnfalse;}intmain(){ios::sync_with_stdio(false);cin.tie(nullptr);while(cinN){intcntN*(N-1)/2;vectorintdiffs(cnt);for(inti0;icnt;i)cindiffs[i];sort(diffs.begin(),diffs.end());if(diffs.empty()||diffs[0]0){coutIncorrect Balance.\n;continue;}maxValdiffs.back();multisetintms(diffs.begin(),diffs.end());// 删除最大差值对应0和maxVal之间的距离autoitms.find(maxVal);if(itms.end()){coutIncorrect Balance.\n;continue;}ms.erase(it);vectorintpoints{0,maxVal};if(dfs(ms,points)){sort(points.begin(),points.end());for(inti0;iN;i){if(i)cout ;coutpoints[i];}cout\n;}else{coutIncorrect Balance.\n;}}return0;}总结本题是一类经典的距离几何重建问题核心解法是回溯搜索借助多重集合管理剩余距离并利用最大距离确定两端点将候选点限制为两种可能有效剪枝。关键技巧在于利用std::multiset维护距离的频次便于检查与恢复。通过预先排序和最大距离确定端点减少搜索空间。优先尝试较大的候选点以符合题目要求的输出顺序。该解法在N≤50N \le 50N≤50且距离值不超过100001000010000的限制下表现良好能够高效求出解或判定无解。理解此类问题的回溯框架对于解决更一般的“从成对距离恢复点集”问题有借鉴意义。
返回列表