算法思维训练之贪心和暴力实战演练(2月5日)

110 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情

A. Cherry (贪心思想)

题意: 给定一个长度为n的序列,a1,a2,...,an{a_1, a_2, ...,a_n},找到这个值的最大值 max(al,al+1,,ar)min(al,al+1,,ar)“max(a_l,a_{l+1},…,a_r)⋅min(a_l,a_{l+1},…,a_r)” ,其中:1l<rn1≤l<r≤n

题解:

结论:

这东西 max(al,al+1,,ar)min(al,al+1,,ar)“max(a_l,a_{l+1},…,a_r)⋅min(a_l,a_{l+1},…,a_r)” 其实等价于这个序列中,相邻的两个数相乘的最大值,即 max(a[i]a[i+1])max(a[i] * a[i + 1])

证明:

以一个长度为3的序列为例:1 3 5

小中大(1 3 5),小大中(1 5 3),中小大(3 1 5),中大小(3 5 1),大中小(5 3 1),大小中(5 1 3)

小中大:small,medium,largesmall, medium, large

① 如果是 {small,medium,largesmall, medium, large},为了让结果更优,可以选择 {medium,largemedium, large}

② 如果是 {small,large,mediumsmall, large, medium},同理① 可以选择 {large,mediumlarge, medium}

③ 如果是 {medium,small,largemedium, small, large},最小值在中间这种情况,其实这个序列等价于{small,largesmall, large},因为 min(al...ar)=smallmin(a_l...a_r) = smallmax(al...ar)=largemax(a_l...a_r) = large

④ 如果是 {large,small,mediumlarge, small, medium},同理③ 等价于{large,smalllarge ,small}

⑤ 如果是 {large,medium,smalllarge, medium, small} 和 {medium,large,smallmedium, large, small},同理①②

证毕。

Code:

const int N = 200010;
int a[N];
signed main()
{
    int _;
    cin >> _;
    while (_--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        int ans = 0;
        for (int i = 1; i <= n - 1; i++) ans = max(ans, a[i] * a[i + 1]); 
        cout << ans << endl;
    }
    return 0;
}

B. Cobb(反向暴力枚举)

题意: 给定一个长度为n的序列 a1,a2,...,an{a_1, a_2, ...,a_n}和一个整数k

找出这个值的最大值:ijk(aiaj)i⋅j−k⋅(a_i|a_j) 其中:1i<jn1≤i<j≤n

数据范围:n(2n105)k(1kmin(n,100))n (2≤n≤10^5),k (1≤k≤min(n,100))

观察到k这个取值范围,最大只能是100。

那么要求这个值 ijk(aiaj)i⋅j−k⋅(a_i|a_j) 的最大值。

那么便是让:(ij)(i⋅j) 尽可能大,让(k(aiaj))(k⋅(a_i|a_j)) 尽可能小。

再观察到 i, j 的取值范围 1i<jn1051≤i<j≤n≤10^5,那么当 i,j 足够大的时候,(k(aiaj))(k⋅(a_i|a_j)) 就没影响了。

所以只需要从后往前枚举,求最大值即可。

Code:

const int N = 200010;
int a[N];
signed main()
{
    int _;
    cin >> _;
    while (_--) {
        int n, k;
        cin >> n >> k;
        for (int i = 1; i <= n; i++) cin >> a[i];
        int ans = -inf;
        for (int i = n; i >= 1; i--) {
            for (int j = i - 1; j >= 1; j--) {
                if (i * j <= ans) break;
                else ans = max(ans, i * j - k * (a[i] | a[j]));
            }
        }
        cout << ans << endl;
    }
    return 0;
}

有些人说,循环前150,前200就可以了,这个要通过详细的计算一下,与题目数据也相关,有可能会被hack的(我没试过,在这也不多bb)