洛谷P1509 找啊找啊找GF题解

题面

题目背景

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

“诶,别再见啊…”

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

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

题目描述

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

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

sqybi 现在有 m 块大洋,他也通过一段时间的努力攒到了 r 的人品。他凭借这些大洋和人品可以泡到一些 MM。他想知道,自己泡到最多的 MM 花费的最少时间是多少。

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

输入格式

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

接下来有 n 行,依次表示编号为1,2,3,…,n 的一个 MM 的信息。每行表示一个 MM 的信息,有三个整数:rmb,rp 和 time。

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

输出格式

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

输入输出样例

输入 #1复制

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

输出 #1复制

13

说明/提示

sqybi 说:如果题目里说的都是真的就好了…

sqybi 还说,如果他没有能力泡到任何一个 MM,那么他就不消耗时间了(也就是消耗的时间为 0)

【数据规模】

对于 20% 的数据,1≤n≤10。
对于 100% 的数据,1≤rmb≤100,1≤rp≤100,1≤time≤1000。
对于 100%

的数据,1≤m,r,n≤100。

分析:

这一道题是一道典型的dp背包题,题中说他已经做好一个列表,里面写着泡每个MM他要花费的钱、人品、时间是多少,所以这是01背包问题

状态转移方程 :

1.先考虑二维的01背包,由题可知,泡每个MM他要花费的钱、人品,数组最大为100*1000,二维数组能开那么大,加一个代表人数的二维数组就可以了,最多也就消耗200000的空间,可以采取这种方法

2.再考虑一维的01背包,只有代表人品的维度,虽然数组能开,但是却少了一个代表钱的维度,不可以使用这种方法

3.考虑交替滚动,此题跟一维一样,不可以使用

综上所述,本题采用二维的01背包

二维01背包:

本题需分类讨论,如果泡比不泡的人数要多,那么可以泡这个MM,人数数组等于上一次泡的人数并能继续泡MM的人数加1,时间数组等于上一次泡的时间并能继续泡MM的人数加泡这一个MM所需的时间,如果泡比不泡的人数相等,那么,时间数组等于上一次泡的时间并能继续泡MM的人数加泡这一个MM所需的时间与上一次泡的时间的最大值,也就是:

for(int i=n;i>=1;i--){
	for(int j=m;j>=b[i];j--){
        for(int k=t;k>=c[i];k--){
			if(s[j][k]<s[j-b[i]][k-c[i]]+1){
               	s[j][k]=s[j-b[i]][k-c[i]]+1;
                dp[j][k]=dp[j-b[i]][k-c[i]]+a[i];
            }
            else if(s[j][k]==s[j-b[i]][k-c[i]]+1){
                dp[j][k]=min(dp[j][k],dp[j-b[i]][k-c[i]]+a[i]);
            }
		}
	}
}

注意:

第一个if判断中的小于号不要写成小于等于

顺序:

第一个for循环遍历MM,第二个for循环遍历钱数,第三个for循环遍历人品,注意遍历人品时要从后往前遍历不然就成了完全背包了

输出

最后需要输出的结果是dp[m][t],表示用m大洋t人品泡到最多的 MM 花费的最少时间

代码:

​
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<algorithm>
#define ll long long
using namespace std;
int n,m,t,dp[1005][1005],a[105],b[105],c[105],s[1005][1005];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>b[i]>>c[i]>>a[i];
	}
	cin>>m>>t;
	for(int i=n;i>=1;i--){
		for(int j=m;j>=b[i];j--){
			for(int k=t;k>=c[i];k--){
				if(s[j][k]<s[j-b[i]][k-c[i]]+1){
                	s[j][k]=s[j-b[i]][k-c[i]]+1;
                    dp[j][k]=dp[j-b[i]][k-c[i]]+a[i];
                }else if(s[j][k]==s[j-b[i]][k-c[i]]+1){
                    dp[j][k]=min(dp[j][k],dp[j-b[i]][k-c[i]]+a[i]);
                }
			}
		}
	}
	cout<<dp[m][t];
	return 0;
}

​

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值