Luogu P1509 找啊找啊找GF 背包问题和次要性DP

找啊找啊找GF

题目背景

“找啊找啊找 GF,找到一个好 GF,吃顿饭啊拉拉手,你是我的好 GF。再见。”

“诶,别再见啊…”

七夕… 七夕… 七夕这个日子,对于 sqybi 这种单身的菜鸟来说是多么的痛苦… 虽然他听着这首叫做“找啊找啊找 GF”的歌,他还是很痛苦。为了避免这种痛苦,sqybi 决定要给自己找点事情干。他去找到了七夕模拟赛的负责人 zmc MM,让她给自己一个出题的任务。经过几天的死缠烂打,zmc MM 终于同意了。

但是,拿到这个任务的 sqybi 发现,原来出题比单身更让人感到无聊 -_- … 所以,他决定了,要在出题的同时去办另一件能够使自己不无聊的事情——给自己找 GF。

题目描述

sqybi 现在看中了 n n n 个 MM,我们不妨把她们编号 1 1 1 n n n。请 MM 吃饭是要花钱的,我们假设请 i i i 号 MM 吃饭要花 r m b [ i ] rmb[i] rmb[i] 块大洋。而希望骗 MM 当自己 GF 是要费人品的,我们假设请第 i i i 号 MM 吃饭试图让她当自己 GF 的行为(不妨称作泡该 MM)要耗费 r p [ i ] rp[i] rp[i] 的人品。而对于每一个 MM 来说,sqybi 都有一个对应的搞定她的时间,对于第 i i i 个 MM 来说叫做 t i m e [ i ] time[i] time[i]。sqybi 保证自己有足够的魅力用 t i m e [ i ] time[i] time[i] 的时间搞定第 i i i 个 MM _

sqybi 希望搞到尽量多的 MM 当自己的 GF,这点是毋庸置疑的。但他不希望为此花费太多的时间(毕竟七夕赛的题目还没出),所以他希望在保证搞到 MM 数量最多的情况下花费的总时间最少。

sqybi 现在有 m m m 块大洋,他也通过一段时间的努力攒到了 r r r 的人品(这次为模拟赛出题也攒 rp 哦~~)。他凭借这些大洋和人品可以泡到一些 MM。他想知道,自己泡到最多的 MM 花费的最少时间是多少。

注意 sqybi 在一个时刻只能去泡一个 MM ——如果同时泡两个或以上的 MM 的话,她们会打起来的…

输入格式

输入的第一行是 n n n,表示 sqybi 看中的 MM 数量。

接下来有 n n n 行,依次表示编号为 1 , 2 , 3 , … , n 1, 2, 3, \ldots , n 1,2,3,,n 的一个 MM 的信息。每行表示一个 MM 的信息,有三个整数: r m b rmb rmb r p rp rp t i m e time time

最后一行有两个整数,分别为 m m m r r r

输出格式

你只需要输出一行,其中有一个整数,表示 sqybi 在保证 MM 数量的情况下花费的最少总时间是多少。

样例 #1

样例输入 #1

4
1 2 5
2 1 6
2 2 2
2 2 3
5 5

样例输出 #1

13

【数据规模】

对于 20 % 20 \% 20% 的数据, 1 ≤ n ≤ 10 1 \le n \le 10 1n10
对于 100 % 100 \% 100% 的数据, 1 ≤ r m b ≤ 100 1 \le rmb \le 100 1rmb100 1 ≤ r p ≤ 100 1 \le rp \le 100 1rp100 1 ≤ t i m e ≤ 1000 1 \le time \le 1000 1time1000
对于 100 % 100 \% 100% 的数据, 1 ≤ m , r , n ≤ 100 1 \le m, r, n \le 100 1m,r,n100


解法1:使用两个DP数组。
由于我们需要同时保证数量最多和时间最小两个条件,那么我们就开两个dp数组,可以使得f[i][j][k](未优化)表示从前i个选,总钱数为j,总人品为k,接下来的更新就和01背包一样,并且我们直接将其优化为2维。

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int f1[N][N];
int f2[N][N];
int main(){
    int n;cin >> n;
    vector<int>rmb(n + 1);
    vector<int>rp(n + 1);
    vector<int>time(n + 1);
    
    for(int i = 1;i <= n;i++){
        cin >> rmb[i] >> rp[i] >> time[i];
    }
    int m,r;cin >> m >> r;
    for(int i = 1;i <= n;i++){
        for(int j = m;j >= rmb[i];j--){
            for(int k = r;k >= rp[i];k--){
            if(f1[j][k] < f1[j - rmb[i]][k - rp[i]]  + 1){
                f1[j][k] = f1[j - rmb[i]][k - rp[i]] + 1;
                f2[j][k] = f2[j - rmb[i]][k - rp[i]] + time[i];
            }else if(f1[j][k] == f1[j - rmb[i]][k - rp[i]] + 1){
                f2[j][k] = min(f2[j][k],f2[j - rmb[i]][k - rp[i]] + time[i]);
            }
        }
    }
    }
    cout << f2[m][r] << endl;
    
    return 0;
}

观察上述代码就会发现花费时间的重要性是次要于人数的重要性的

解法2:次要性dp
如果知道了会有重要性之间的差距,那么我们就可以通过操纵二者的权值,实现对二者主要和次要的体现。

在这里我们实现的方式就是将求主要:最大数量,次要:最小时间转化为求一个很大的数*数量 - 时间
因为在这道题里体现的是数量更为重要,那么我们将数量的价值放大,并且这里的单位数量为1。

这里对于很大的数怎么求,我们可以将其设定为所有单独时间的和,这样能够稳定的保证数量一定得到更大的价值。

在最后我们通过倒推这个式子就能够得到最后要求的时间。

#include<bits/stdc++.h>
using namespace std;
const int N = 110;

int f[N][N];

int main(){
    int n;cin >> n;
    vector<int>rmb(n + 1);
    vector<int>rp(n + 1);
    vector<int>time(n + 1);
    
    vector<int>w(n+1);
    
    for(int i = 1;i <= n;i++){
        cin >> rmb[i] >> rp[i] >> time[i];
        w[i] = 100100 - time[i];
    }
    
    int m,r;cin >> m >> r;
    for(int i = 1;i <= n;i++){
        for(int j = m;j >= rmb[i];j--){
            for(int k = r;k >= rp[i];k--){
                f[j][k] = max(f[j][k],f[j-rmb[i]][k - rp[i]] + w[i]);
            }
        }
    }
    cout << (f[m][r]/100100 + 1)*100100 - f[m][r];
    
    return 0;
}

虽然但是,这个方法有点玄学了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值