洛谷题单指南-暴力枚举-P2392 kkksc03考前临时抱佛脚

原题链接:https://www.luogu.com.cn/problem/P2392

题意解读:由于可以同时计算两道同一科的题目,只需要把某一科题目分两堆,使得两堆总时长之差最小,时长较大的一堆就是完成这一科的最短时间。

解题思路:

既然知道了要把一科题目分两堆,关键是如何分堆呢?

比较容易犯的错是用贪心来解题:把一科题目按时长排序,从小到大取出放入一堆,剩余的规到另一堆,计算两堆之差最小的点。

贪心法的问题很容易举出反例,比如某一科题目时长为:1、3、9、11,如果按此方法,得到最短时长为13,而实际是1+11 -(3+9)之差最小,最短时长应该是12。

既然贪心不好使,就直接使用枚举子集的方式,对于某一科s个题目,每一道题目选或者不选,来划分成两组,计算两组和的差值,取差值最小的一组和作为最短时长。

前面讲过,枚举子集可以采用二进制法,也可以使用DFS,这里采用二进制法。DFS的代码参考:https://www.cnblogs.com/jcwy/p/18052141

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 25;

int s1, s2, s3, s4;
int a[N], b[N], c[N], d[N];

//枚举所有的子集,计算子集和与剩余元素和之差的绝对值最小时,较大的那一组元素和
int gettime(int e[N], int len)
{
    int res = 0, minx = 2e9;
    for(int i = 0; i < pow(2, len); i++) // 对于长度为len的数组,从中选数的方案是0 ~ 2^len - 1对应的二进制,1表示选,0表示不选
    {
        int sum1 = 0, sum2; //sum1表示选择的元素之和,sum2表示剩余元素之和
        for(int j = 0; j < len; j++) //遍历每一个二进制位
        {
            if(i >> j & 1) //如果第j+1个二进制位是1
            {
                sum1 += e[j + 1]; //选择第j+1个数
            }
        }
        sum2 = e[0] - sum1; //计算剩余元素之和
        if(abs(sum1 - sum2) < minx) 
        {
            minx = abs(sum1 - sum2); //记录选择元素和与剩余元素和之差绝对值的最小值
            res = max(sum1, sum2); //当两组和相差最小时,较大的数即为答案
        }
    }
    return res;
}

int main()
{
    cin >> s1 >> s2 >> s3 >> s4;
    for(int i = 1; i <= s1; i++) 
    {
        cin >> a[i];
        a[0] += a[i]; //a[0]是元素之和
    }
    for(int i = 1; i <= s2; i++) 
    {
        cin >> b[i];
        b[0] += b[i]; //b[0]是元素之和
    }
    for(int i = 1; i <= s3; i++) 
    {
        cin >> c[i];
        c[0] += c[i]; //c[0]是元素之和
    }
    for(int i = 1; i <= s4; i++) 
    {
        cin >> d[i];
        d[0] += d[i]; //d[0]是元素之和
    }
    
    cout << gettime(a, s1) + gettime(b, s2) + gettime(c, s3) + gettime(d, s4);

    return 0;
}

 

posted @ 2024-02-02 14:13  五月江城  阅读(86)  评论(0编辑  收藏  举报