题解是 GPT 写的。

题意抽象

nn 根小棒里选 6 根互不重复,分成两组各 3 根,每组能组成三角形(满足“两边和大于第三边”),目标是让两三角形周长之和最大。若无解输出 1-1

“必须顺次首尾相连”对长度题来说等价于普通三角形判定,不需要额外处理角度/顺序。


关键观察 1:排序后,某个最大边的最优三角形一定是相邻三根

把长度升序排序:a1a2ana_1 \le a_2 \le \dots \le a_n

若想用 aia_i 作为三角形最大边,为了让周长最大,另外两条边应尽量大,也就是选 ai1,ai2a_{i-1}, a_{i-2}

并且有一个重要性质:

  • 若相邻三根 ai2+ai1aia_{i-2}+a_{i-1} \le a_i,则任何两根 ai1\le a_{i-1} 的棒和 aia_i 都不可能成三角形(因为换更小的只会让左边更小)。

所以:

  • “以 aia_i 为最大边的三角形存在” 当且仅当 相邻三根 ai2,ai1,aia_{i-2},a_{i-1},a_i 满足

    ai2+ai1>aia_{i-2}+a_{i-1} > a_i
  • 同时这相邻三根就是该最大边下的最大周长选择。

因此我们只需要关注所有满足 a[i] < a[i-1] + a[i-2] 的位置 ii(代码里用 b[] 记录这些 “可作为一个相邻三角形结尾的下标”)。


关键观察 2:两三角形最大周长的候选结构

b[m] 是最大的可行结尾下标,那么相邻三角形 (b[m]2,b[m]1,b[m])(b[m]-2,b[m]-1,b[m]) 是“尽量靠后/尽量大”的一个三角形。

求两个互不重叠三角形时,有两类典型最优情况:

情况 A:两个三角形在排序数组中是两段互不重叠的相邻三根

如果我们决定第二个三角形用结尾 b[m](最大的那个),为了不重叠,另一个三角形的结尾下标必须 b[m]3\le b[m]-3
那么第一个三角形也应该尽量大 —— 即在所有满足 b[m] - b[i] >= 3b[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 根棒的“前三根”本身不能成三角形,但仍能通过交叉分组形成两个三角形,例如:

1,1,2,2,2,21,1,2,2,2,2

前三根 1,1,21,1,2 不满足严格不等式,但可以分成 (1,2,2)(1,2,2)(1,2,2)(1,2,2)

因此仅用“两个相邻三根块”会漏解。

不过注意:如果我们已经决定“用最后 6 根”,那么周长和就是这 6 根之和,剩下只要判断能否把这 6 根拆成两个三角形即可。
6 根棒划分成 3+33+3 的不同方式只有

(63)2=10\frac{\binom{6}{3}}{2} = 10

种,完全可以常数枚举(代码里就是把这 10 种拆法逐个判断)。

这就是代码后半段对 t1..t6 的 10 个 if


算法流程总结

  1. 读入 nn 和数组 aa,升序排序。

  2. 扫描 i=3..ni=3..n,把所有满足 a[i] < a[i-1] + a[i-2]ii 记到 b[](表示相邻三根可成三角形)。

  3. 候选答案 1(情况 A):

    • 用最大的 b[m] 作为第二个三角形结尾;

    • 找最大的 b[i] 使 b[m]-b[i]>=3 作为第一个三角形结尾;

    • 计算两段相邻三根周长和。

  4. 候选答案 2(情况 B):

    • b[m] >= 6,取最后 6 根 a[b[m]-5..b[m]],枚举 10 种拆分,看能否形成两个三角形;

    • 若能,候选为这 6 根之和。

  5. 取两者最大;若始终不存在解或 n<6n<6,输出 1-1


复杂度与精度

  • 排序:O(nlogn)O(n\log n)

  • 线性扫描记录可行三角形:O(n)O(n)

  • 最后 6 根枚举分法:常数 1010 次判断
    总体可用于很大的 nn


代码是很久以前写的,不好看。

#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;
}