找啊找啊找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
1≤n≤10;
对于
100
%
100 \%
100% 的数据,
1
≤
r
m
b
≤
100
1 \le rmb \le 100
1≤rmb≤100,
1
≤
r
p
≤
100
1 \le rp \le 100
1≤rp≤100,
1
≤
t
i
m
e
≤
1000
1 \le time \le 1000
1≤time≤1000。
对于
100
%
100 \%
100% 的数据,
1
≤
m
,
r
,
n
≤
100
1 \le m, r, n \le 100
1≤m,r,n≤100。
解法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;
}
虽然但是,这个方法有点玄学了。