- P47's solution
-
P47's Solution
- @ 2025-9-5 22:24:09
题解是 GPT 写的。
题意抽象
从 根小棒里选 6 根互不重复,分成两组各 3 根,每组能组成三角形(满足“两边和大于第三边”),目标是让两三角形周长之和最大。若无解输出 。
“必须顺次首尾相连”对长度题来说等价于普通三角形判定,不需要额外处理角度/顺序。
关键观察 1:排序后,某个最大边的最优三角形一定是相邻三根
把长度升序排序:。
若想用 作为三角形最大边,为了让周长最大,另外两条边应尽量大,也就是选 。
并且有一个重要性质:
- 若相邻三根 ,则任何两根 的棒和 都不可能成三角形(因为换更小的只会让左边更小)。
所以:
-
“以 为最大边的三角形存在” 当且仅当 相邻三根 满足
-
同时这相邻三根就是该最大边下的最大周长选择。
因此我们只需要关注所有满足 a[i] < a[i-1] + a[i-2] 的位置 (代码里用 b[] 记录这些 “可作为一个相邻三角形结尾的下标”)。
关键观察 2:两三角形最大周长的候选结构
设 b[m] 是最大的可行结尾下标,那么相邻三角形 是“尽量靠后/尽量大”的一个三角形。
求两个互不重叠三角形时,有两类典型最优情况:
情况 A:两个三角形在排序数组中是两段互不重叠的相邻三根
如果我们决定第二个三角形用结尾 b[m](最大的那个),为了不重叠,另一个三角形的结尾下标必须 。
那么第一个三角形也应该尽量大 —— 即在所有满足 b[m] - b[i] >= 3 的 b[i] 里取最大的那个 b[mx]。
这样就得到候选答案:
$$(a_{b[m]}+a_{b[m]-1}+a_{b[m]-2})+(a_{b[mx]}+a_{b[mx]-1}+a_{b[mx]-2})$$这对应代码里先算的那段 mx。
情况 B:最优解可能用“最后 6 根”,但分组不是 (前三)+(后三)
有时最后 6 根棒的“前三根”本身不能成三角形,但仍能通过交叉分组形成两个三角形,例如:
前三根 不满足严格不等式,但可以分成 和 。
因此仅用“两个相邻三根块”会漏解。
不过注意:如果我们已经决定“用最后 6 根”,那么周长和就是这 6 根之和,剩下只要判断能否把这 6 根拆成两个三角形即可。
6 根棒划分成 的不同方式只有
种,完全可以常数枚举(代码里就是把这 10 种拆法逐个判断)。
这就是代码后半段对 t1..t6 的 10 个 if。
算法流程总结
-
读入 和数组 ,升序排序。
-
扫描 ,把所有满足
a[i] < a[i-1] + a[i-2]的 记到b[](表示相邻三根可成三角形)。 -
候选答案 1(情况 A):
-
用最大的
b[m]作为第二个三角形结尾; -
找最大的
b[i]使b[m]-b[i]>=3作为第一个三角形结尾; -
计算两段相邻三根周长和。
-
-
候选答案 2(情况 B):
-
若
b[m] >= 6,取最后 6 根a[b[m]-5..b[m]],枚举 10 种拆分,看能否形成两个三角形; -
若能,候选为这 6 根之和。
-
-
取两者最大;若始终不存在解或 ,输出 。
复杂度与精度
-
排序:
-
线性扫描记录可行三角形:
-
最后 6 根枚举分法:常数 次判断
总体可用于很大的 。
代码是很久以前写的,不好看。
#include<bits/stdc++.h>
using namespace std;
int n,m;
double a[1048577];
int b[1048577];
double ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lf",&a[i]);
sort(a+1,a+n+1);
for(int i=3;i<=n;i++)
if(a[i]<a[i-1]+a[i-2])
b[++m]=i;
int mx=-1;
for(int i=1;i<=m;i++)
if(b[m]-b[i]>=3)
mx=i;
if(mx!=-1)
ans=a[b[m]]+a[b[m]-1]+a[b[m]-2]+a[b[mx]]+a[b[mx]-1]+a[b[mx]-2];
if(b[m]>=6)
{
double t1=a[b[m]-5],t2=a[b[m]-4],t3=a[b[m]-3],t4=a[b[m]-2],t5=a[b[m]-1],t6=a[b[m]];
double tt=t1+t2+t3+t4+t5+t6;
if(t1+t2>t3&&t4+t5>t6) ans=tt;
if(t1+t2>t4&&t3+t5>t6) ans=tt;
if(t1+t2>t5&&t3+t4>t6) ans=tt;
if(t1+t2>t6&&t3+t4>t5) ans=tt;
if(t1+t3>t4&&t2+t5>t6) ans=tt;
if(t1+t3>t5&&t2+t4>t6) ans=tt;
if(t1+t3>t6&&t2+t4>t5) ans=tt;
if(t1+t4>t5&&t2+t3>t6) ans=tt;
if(t1+t4>t6&&t2+t3>t5) ans=tt;
if(t1+t5>t6&&t2+t3>t4) ans=tt;
}
if(ans==0||n<6) puts("-1");
else printf("%.3lf\n",ans);
return 0;
}