[DFS/子集枚举]洛谷补题[P2392 kkksc03考前临时抱佛脚]

题目链接

题意:

有四门学科,每个学科最多二十个任务,每次最多同时进行一门学科的两个任务,给出各学科各任务的时间,求做完全部任务所需最小的时间

样例解释:
输入
1 2 1 3		
5
4 3
6
2 4 3

输出
20

第一门学科用时5分钟,第二门学科两门同时做,总共用时4分钟,第三门学科用时6分钟,第四门学科,做(2 + 3) 和 4, 即做2的同时做4, 做完2后一边继续做3, 一边继续做4,总共用时5分钟,总共用时 5 + 4 + 6 + 5 = 20 分钟

分析:

如何让总用时最小?

由于只能一门一门学科做,当每一门学科都用时最小的时候,总用时也最小。

如何让每一门学科用时最小?

每次最多同时做两个任务,可以将全部任务划分为两个集合,让两个集合的时间之和尽可能接近(靠近n / 2), 此时这一门学科用时最小

即通过子集枚举,找到较小(<= n / 2)且最大的一个子集当前学科所需要的最少时间,即较大(>= n / 2)的子集的时间和, 也即当前学科所需总时间 - 较小子集的总时间

分别对每一门学科进行子集枚举(使用DFS), 在[1, num]区间(num为当前学科任务的个数)遍历,每个元素都有两种情况,选 or 不选。

: 如果选择了当前的任务总时间仍然不超过 n / 2(较小的子集), 就将当前任务的时间加入集合并递归下一层。在搜索结束的时候与当前最大的子集和比较,若当前方案更优则更新最大的子集和(较小的子集和最大)

不选:如果不选,直接递归下一层即可

参考代码
#include <bits/stdc++.h>

using namespace std;

#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int s[5]; //存4个学科的任务个数
int t[30]; //存每个学科各个任务的时间

int maxtime; //当前学科 总时间之和 <= n / 2 并且 尽可能大的 集合的总时间(即较小集合中时间总和)
int nowtime; //暂存 较小的集合的时间总和的 当前方案
int num;//当前学科的任务数量
int cnt;//当前学科的总时间

void dfs(int x) //dfs做的是子集枚举, x是递归层数, 枚举区间为[1, sum]
{
    //边界条件: 当前递归的层数超过了当前学科的任务数量num
    if(x > num)
    {
        maxtime = max(maxtime, nowtime); //如果当前子集的总时间nowtime更大则更新maxtime
        return;
    }
    
    //每一个元素有两种选择,选和不选

    //选
    if(t[x] + nowtime <= cnt / 2) //选择的条件: 小于总时间的一半
    {
        nowtime += t[x];
        dfs(x + 1); //递归下一层
        nowtime -= t[x]; //恢复现场
    }
    
    //不选
    dfs(x + 1);
}
int main()
{
    IOS
    cin >> s[1] >> s[2] >> s[3] >> s[4]; //四个学科各自的任务数

    int ans = 0; //记录全部学科的最终最少总用时
    for(int i = 1; i <= 4; i++) //分别枚举四个学科的时间情况
    {
    	//每一个学科需要初始化的变量
        nowtime = 0;
        cnt = 0; //每一个学科的任务总用时
        num = s[i]; //num表示的是当前学科有几项任务, 即s[i]
        maxtime = 0; //maxtime 记录的是当前学科较小的那一个子集总时间和, 用dfs去做子集枚举
        
        for(int j = 1; j <= s[i]; j++) //每个学科有s[i]个任务
        {
            cin >> t[j];
            cnt += t[j]; //cnt表示当前科目总共需要花的时间
        }
        
        dfs(1); //对当前学科通过深搜进行子集枚举,目的是将其全部任务分成两个子集, 让他们尽可能接近(枚举一个使其<= n / 2 且尽可能大),dfs后maxtime记录了两子集中较小的子集的总时间,用总时间 - maxtime就是当前任务最少的所需要花的时间(两个集合中的max)
        
        ans += cnt - maxtime;   //当前学科的最小用时,累加起来即为总的最小用时
    }
    cout << ans << endl;
    return 0;
}

The End

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值