小明的打工计划(动态规划--两种方法)

小明的打工计划

(仅作为菜鸡的记录)
题目描述
作为一个有很多游戏想买但囊中羞涩的大学生,小明决定在这个暑假开始打工赚钱。经过一段时间的寻找,他一共找到了n个打工的招聘广告,其中第i个打工的日期从li开始,到ri为止,一共付给他ci元钱。因为这些打工的时间都相互冲突,所以同一天小明最多参加一个打工,并且一个打工一旦开始,就必须一直工作到结束,不能中途退出。现在小明想要知道,这个暑假他打工最多能得到多少钱?

输入数据
第一行一个整数n(1≤n≤1000000),表示招聘广告的数量。 接下来一共n行,每行3个整数li,ri,ci(1≤li≤ri≤1000000,1≤ci≤1000000000),表示打工的开始时间,结束时间和报酬。
输出数据
一行一个整数k,表示小明最多能够得到的钱数。
样例输入
3
1 2 3
3 4 3
2 3 5
样例输出
6

方法一:

先对结束时间排序,然后遍历找最优解。
dp[i] 代表结束时间 i 前的最大收益
由于结束时间等于i的可能有多个项目,所以初始时dp[i] = dp[i-1],
状态转移方程为:dp[i] = max( dp[i], dp[l-1] + c),
l就是当前项目开始的时间。

(个人觉得方法一更简单些,测试过可以AC)

#include <iostream>
#include <algorithm>
using namespace std;

typedef long long int ll;
const int maxn = 1000000 + 5;
ll dp[maxn] = {0}; //dp[i]表示结束时间为i的当前最大收益 

typedef struct{
	int l;
	int r;
	ll c;
}rate;
rate d[maxn];

bool cmp(rate a, rate b){
	return a.r < b.r;
}

int main(){
	int n;
	cin>>n;
	for(int i=0; i<n; i++){
		cin>>d[i].l>>d[i].r>>d[i].c;
	}
	sort(d, d+n, cmp);
	int max_end = d[n-1].r;
	//cout<<max_end<<endl;
	
	int curi = 0;
	for(int e=1; e<=max_end; e++){
		dp[e] = dp[e-1]; 
		while(d[curi].r == e){
			int curl = d[curi].l;
			int curc = d[curi].c;
			dp[e] = max(dp[e], dp[curl-1] + curc);
			curi++;	 //在全部结束日期为e的里面找最优的
		}
	}
	ll maxz = 0;
	for(int i=0; i<=max_end; i++){
		if(dp[i] > maxz){
			maxz = dp[i];
		}
	}
	cout<<maxz<<endl;
	
	return 0;
} 

方法二:

dp[i][0] 代表第i个项目不做时的最优收益,
dp[i]1] 代表第i个项目做时的最优收益,

dp[i][0] = max( dp[i-1][0], dp[i-1][1])
//s代表 当前项目的开始时间 之前的全部项目数量
dp[i][1] = max( dp[s-1][0], dp[s-1][1]) + c
这种方法网上 其他大佬都用了树状数组,但由于本菜鸡实在没看懂树状数组怎么在求和,所以整了个数组来记录结束时间r之前的全部项目数量,(可以AC 还好没超时)!!

#include <iostream>
#include <algorithm>
using namespace std;

typedef long long int ll;
const int maxn = 1000000 + 5;

typedef struct{
	int l;
	int r;
	ll c;
}rate;
rate d[maxn];

bool cmp(rate a, rate b){
	return a.r < b.r;
}

ll dp[maxn][2] = {0};
ll list[maxn] = {0};  //list[i]代表计算结束时间为i前的全部项目数!!!可以替代树状数组 
 
int main(){
	int n;
	cin>>n;
	for(int i=0; i<n; i++){
		cin>>d[i].l>>d[i].r>>d[i].c;
	}
	sort(d, d+n, cmp);
	dp[0][0] = 0;
	dp[0][1] = d[0].c;
	list[d[0].r]++;
	int last_e = d[0].r;
	for(int i=1; i<n; i++){
		int cur_e = d[i].r;  //当前项目的结束时间 
		if(cur_e == last_e){
			list[cur_e]++;
		}
		else{
			list[cur_e] = list[last_e];
			list[cur_e]++;
			last_e = cur_e;
		}
	}
	int cur = -1;
	for(int i=0; i<maxn; i++){
		if(list[i] != 0){
			cur = list[i];
		}else{
			list[i] = cur;
		}
	}//主要是针对这种情况:结束时间7没有出现过list[7]中就为0,但结束时间6出现过,所以list[7]=list[6] 

	for(int i=1; i<n; i++){
		dp[i][0] = max(dp[i-1][0], dp[i-1][1]);
		int s = list[(d[i].l - 1)];  //s代表 当前项目的开始时间 之前的全部项目数量 
		//cout<<s<<endl;
		if(s == -1){
			dp[i][1] = d[i].c;  //表示d[i].l之前没有项目发生 
		}
		else{
			dp[i][1] = max(dp[s-1][0], dp[s-1][1]) + d[i].c;  
			//max(dp[s-1][0], dp[s-1][1])已经代表当前项目开始时间前全部项目的最优收益了 
		}
	}
	ll ans = max(dp[n-1][0], dp[n-1][1]);
	cout<<ans<<endl;
	/* 
	for(int i=1; i<= d[n-1].r; i++){
		cout<<list[i]<<" ";
	}
	*/
	return 0;
}

如有问题,恳请批评指正!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值