【排序(运算符重载/自定义比较函数)+01背包(动态规划)+快读快写】2024睿抗-5837. 工作安排

题目描述

KKKNNN 项工作等待完成,第 iii 项工作需要花 tit_iti 单位时间,必须在 did_idi 时刻或之前完成,报酬为 pip_ipi

假设小 KKK 工作时刻从 000 开始,且同一时刻只能做一项工作、工作一旦开始则不可中断或切换至其他工作,请你帮小 KKK 规划一下如何选择合适的工作,使小 KKK 可以获得最多的报酬。

输入格式

输入第一行是一个正整数 TTT,表示数据的组数。

接下来有 TTT 组数据,每组数据第一行是一个正整数 NNN,表示待完成工作的数量。

接下来的 NNN 行,每行三个非负整数 ti,di,pit_i,d_i,p_iti,di,pi,表示第 iii 项工作需要花费的时间、截止时间以及报酬。

输出格式

对于每组数据,输出小 KKK 能获得最多的报酬是多少。

数据范围

1≤T≤51 \le T \le 51T5,
1≤N≤50001 \le N \le 50001N5000,
0≤ti,di,pi≤50000 \le t_i,d_i,p_i \le 50000ti,di,pi5000

输入样例:
3
5
1 2 50
3 3 100
1 5 1
3 2 5000
4 5 30
5
1 2 50
3 3 20
1 5 1
3 2 5000
4 5 30
5
1 2 50
3 3 100
1 5 1
3 2 5000
5 5 800
输出样例:
101
80
800

二维dp(6/10)内存超限代码

/*
max报酬 = 规定时间完成 + 完成的任务报酬尽量高
花的时间t和截止时间d可以做一个统一->最迟开始时间 (d-t), 在此时间之后便不能加入背包
(开始时间_i + t_i) 之间不能有重复
0-1背包 + 贪心
*/


// 在动态规划中,按截止时间排序是为了确保状态转移的无后效性。
#include <bits/stdc++.h>
using namespace std;

int rd()
{
    int k = 0;
    char c = getchar();
    while (!isdigit(c))
        c = getchar();
    while (isdigit(c))
        k = (k << 1) + (k << 3) + (c ^ '0'), c = getchar();
    return k;
}

void wr(int x)
{
    if (x > 9)
        wr(x / 10);
    putchar((x % 10) ^ '0');
}

// 定义工作任务的结构体
struct Job {
    int t, d, p;  // t是完成任务需要的时间,d是截止时间,p是完成后的报酬
    
    // 为了能对工作按截止时间排序,重载小于运算符
    bool operator<(const Job& other) const {
        return d < other.d;  // 截止时间早的工作排在前面
    }
};

// 解决一组任务选择问题的函数
void solve() {
    int n;
    cin >> n;  // 读入任务的数量
    int max_d = 0; // 注意这里需要清空
    
    // 存储所有任务,注意这里使用1-based索引(从1开始)
    vector<Job> jobs(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> jobs[i].t >> jobs[i].d >> jobs[i].p;  // 读入每个任务的时间、截止和报酬
        max_d = max(max_d,jobs[i].d);
    }
    
    // 按截止时间对任务进行排序
    // 就像我们做事情,先做截止时间早的,这样不容易耽误后面的事
    sort(jobs.begin() + 1, jobs.end());

    // dp数组是整个程序的核心!
    // dp[i][j]表示:考虑前i个工作,在时间j前能获得的最大报酬
    // 比如dp[3][10]就是考虑前3个工作,在时间10前能赚到的最多钱
    vector<vector<int>> dp(n + 1, vector<int>(max_d + 1, 0));

    // 遍历每个工作,逐步计算最优解
    for (int i = 1; i <= n; i++) {
        int t = jobs[i].t, d = jobs[i].d, p = jobs[i].p;  // 当前工作的三个属性
        // if (t > d) continue; 这句话不能加,因为后面的dp[i-1][j]需要更新
        
        for (int j = 0; j <= max_d; j++) {
            // 情况1:不选择做第i个工作
            // 那么能得到的最大报酬就和只考虑前i-1个工作时一样
            dp[i][j] = dp[i-1][j];
            
            // 情况2:选择做第i个工作
            // 我们需要检查在时间j前完成这个工作是否可能
            // j >= t 确保开始做这个工作的时间不会是负数(因为开始时间是j-t)
            // j <= d 确保这个工作能在截止时间前完成
            if(j <= d && t <= j) {
                dp[i][j] = max(dp[i][j], dp[i-1][j - t] + p);
            }
            
        }
    }

    int ans = 0;
    for (int j = 0; j <= max_d; j++) {
        ans = max(ans, dp[n][j]);
    }
    cout << ans << "\n";
}

int main() {
    int t = rd();
    while (t--) {
        solve();
    }
    return 0;
}

一维dp AC代码

/*
max报酬 = 规定时间完成 + 完成的任务报酬尽量高
花的时间t和截止时间d可以做一个统一->最迟开始时间 (d-t), 在此时间之后便不能加入背包
(开始时间_i + t_i) 之间不能有重复
0-1背包 + 贪心
*/


// 在动态规划中,按截止时间排序是为了确保状态转移的无后效性。
#include <bits/stdc++.h>
using namespace std;

int rd(){
    int res = 0;
    char c = getchar();
    while(!isdigit(c)){
        c = getchar();
    }
    while(isdigit(c)){
        res = (res << 1) + (res << 3) + (c^'0'), c = getchar();
    }
    return res;
}

void wr(int x)
{
    if (x > 9)
        wr(x / 10);
    putchar((x % 10) ^ '0');
}

// 定义工作任务的结构体
struct Job {
    int t, d, p;  // t是完成任务需要的时间,d是截止时间,p是完成后的报酬
    
    // // 为了能对工作按截止时间排序,重载小于运算符
    // bool operator<(const Job& other) const {
    //     return d < other.d;  // 截止时间早的工作排在前面
    // }
};

// 解决一组任务选择问题的函数
void solve() {
    int n = rd();
    int max_d = 0; // 注意这里需要清空
    
    // 存储所有任务,注意这里使用1-based索引(从1开始)
    vector<Job> jobs(n + 1);
    for (int i = 1; i <= n; i++) {
        jobs[i].t = rd();
        jobs[i].d = rd();
        jobs[i].p = rd();
        max_d = max(max_d,jobs[i].d);
    }
    
    // 按截止时间对任务进行排序
    // 就像我们做事情,先做截止时间早的,这样不容易耽误后面的事
    sort(jobs.begin() + 1, jobs.end(),
        [](const Job& a,const Job& b){
            return a.d < b.d;
        });

    // dp数组是整个程序的核心!
    // dp[j]表示:考虑前i个工作,在时间j前能获得的最大报酬
    vector<int> dp(max_d + 1, 0);

    // 遍历每个工作,逐步计算最优解
    for (int i = 1; i <= n; i++) {
        int t = jobs[i].t, d = jobs[i].d, p = jobs[i].p;  // 当前工作的三个属性
        if (t > d) continue; 
        
        for (int j = max_d; j >= t; j--) {
            // 情况1:不选择做第i个工作
            // 那么能得到的最大报酬就和只考虑前i-1个工作时一样
            // dp[i][j] = dp[i-1][j];
            
            // 情况2:选择做第i个工作
            // 我们需要检查在时间j前完成这个工作是否可能
            // j >= t 确保开始做这个工作的时间不会是负数(因为开始时间是j-t)
            // j <= d 确保这个工作能在截止时间前完成
            dp[j] = max(dp[j], dp[min(j,d) - t] + p);
        }
    }

    cout << dp[max_d] << "\n";
}

int main() {
    int t = rd();
    while (t--) {
        solve();
    }
    return 0;
}

这里的重载小于函数可以替换为sort函数的自定义比较函数部分!
注意:
在ACwing上提交代码的时候一定需要开C++O2优化才可以通过。
当然读者也可以将vector数组改成普通的定长数组来Ac本题!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值