1 条题解

  • 0
    @ 2025-9-5 22:24:09

    题解是 GPT 写的。

    题意抽象

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

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


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

    把长度升序排序:a1≤a2≤⋯≤ana_1 \le a_2 \le \dots \le a_n。

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

    并且有一个重要性质:

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

    所以:

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

      ai−2+ai−1>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] >= 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 根棒的“前三根”本身不能成三角形,但仍能通过交叉分组形成两个三角形,例如:

    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(nlog⁡n)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;
    }
    
    • 1

    信息

    ID
    47
    时间
    1000ms
    内存
    256MiB
    难度
    6
    标签
    递交数
    41
    已通过
    3
    上传者