竞赛讨论区 > T2关于"错解"其实是最优解的证明
头像
MYCui_
发布于 2020-12-15 22:33
+ 关注

T2关于"错解"其实是最优解的证明

#### 前言
这道题目的解法很多种,这里提供一种看似错误时间复杂度的正确做法以及证明,容我大放狂词,这是本题最优秀的算法,时间复杂度大概是O(n)。

#### 解法

首先把数据给出的 n 条边进行从小到大排序。

相信最大值大家都会,也就是判断第一个满足条件的 a[i] < a[i - 1] + a[i + 2] 的 a[i] + a[i - 1] + a[i - 2]

最小值暗藏玄机。

这是我寻找满足条件的最小周长三角形的做法
```cpp
        for(int i = 3 ; i <= n && flag; i ++)
        {
            if(s[i - 2] + s[i - 1] <= s[i])continue;
            for(int j = 1 ; j < i - 1 && flag; j ++)
            {
                for(int k = j + 1 ; k <= i - 1 && flag; k ++)
                    if(s[j] + s[k] > s[i])Min = s[j] + s[k] + s[i],flag = 0;//其实这里可以换成二分
            }
        }
```
看似是 O(n ^ 3) 的?

实际上是O(常数) 的。

为什么呢?

因为实际上我只需要找到第一个满足条件的三角形即可,找到第一个满足条件的就break。

枚举最大边即可。

$s[i - 2] + s[i - 1] > s[i]$ 则必定存在小于等于$s[i - 2]$ 以及 $s[i - 1] $的$s[j] + s[k] > s[i]$

那么假设我们枚举到的最大边有解,我们就需要寻找一个最小边以及一个次小边使得s[j] + s[k] + s[i]最小,暴力枚举即可。

为什么直接不用二分也能过,而且只跑了**10ms**?

考虑什么情况下此算法复杂度被卡满。

首先考虑第一重循环,我们遇到的第一个有答案的i肯定不超过 100 次。然后时间复杂度就是O(50 + 100 * 100)的,因为第二三重循环都只循环到i。

理性分析,要使得本算法最劣,实际上就是尽量使得第一个有答案的i尽可能大。

首先, $s[i - 1] + s[i - 2] <= s[i]$,要满足这个关系尽可能的久,不难发现我们实际上需要构造一个斐波那契数列。

为什么呢?

证明:
假设最小的边为 x , 第二个为y , 第三个就得大于等于 x + y ,那么第三个就会是大于等于 x + y + y 的,第四个就会是大于等于 x + y + y + y + x 的。

易得,我们第一个为x = 1的时候最劣,也就是构造斐波那契数列,然而a[i]是小于等于1e9的,那么最多枚举i不超过50次,因此得到最小值需要的时间几乎可以忽略!

因此这是最优算法。

```cpp
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 返回在所有合法的三角形的组成中,最大的三角形的周长减去最小的三角形的周长的值
     * @param a int整型vector 代表n个数的大小
     */
    int solve(int n, vector<int>& a) {
        int s[1000005];
        for(int i = 0 ; i < n ; i ++)
            s[i + 1] = a[i];
        sort(s + 1 , s + 1 + n);
        int Min = 0x3f,Ans = 0,Max = -1;
        for(int i = n ; i >= 3 ; i --)
        {
            int op;
            if(s[i - 1] + s[i - 2] <= s[i])continue;
            op = s[i - 1] + s[i - 2] + s[i];
            Max = max(Max,op);
        }
        int flag = 1;
        for(int i = 3 ; i <= n && flag; i ++)
        {
            if(s[i - 2] + s[i - 1] <= s[i])continue;
            for(int j = 1 ; j < i - 1 && flag; j ++)
            {
                for(int k = j + 1 ; k <= i - 1 && flag; k ++)
                    if(s[j] + s[k] > s[i])Min = s[j] + s[k] + s[i],flag = 0;
            }
        }
        Ans = Max - Min;
        return Ans;
    }
};
```

全部评论

(4) 回帖
加载中...
话题 回帖

等你来战

查看全部

热门推荐