POJ 3171 DP + 线段树

157 篇文章 1 订阅
大致题意

农场主需要再 [M, E] 时间段内都至少有 1 头牛在打扫。有 N 头牛,工作时间段为 [T1, T2] ,薪水为 S。求农场主需要提供的最小总工资。0 <= M <= E <= 86,399, M <= T1 <= T2 <= E, 0 <= S <= 500,000

dp[i] 表示覆盖时间段 [M, i] 所需要的最小工资。要对牛按照工作开始时间升序排序,以保证遍历牛的时候其工作时间段及之前的可能值已经处理。用线段树实现的 RMQ 维护 dp 的值,考虑到覆盖的连接,每次查询 [T1 - 1, T2) 的最小值,对 dp 进行更新。

#include <cstdio>
#include <STDLIB.H>
#include <cmath>
#include <map>
#include <algorithm>
#include <iostream>
#define min(a,b)    (((a) < (b)) ? (a) : (b))
#define max(a,b)    (((a) > (b)) ? (a) : (b))
#define abs(x)    ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define eps 1e-4
#define M_PI 3.14159265358979323846
#define MAX_M 86500
#define MAX_N 10000
using namespace std;
struct cow{
	int s, t, salary;
};
int N, M, E;
cow cs[MAX_N];
int dp[MAX_M];

bool cmp(const cow &a, const cow &b){
	return a.s < b.s;
}
//基于线段树的RMQ开始
const int ST_SIZE = (1 << 18) - 1;
int n;
int dat[ST_SIZE];
void rmq_init(int n_){
	n = 1;
	while(n < n_) n *= 2;
	memset(dat, 0x3f, sizeof(int) * (2 * n - 1));
}
void update(int k, int a){
	k += n - 1;
	dat[k] = a;
	while(k > 0){
		k = (k - 1) >> 1;
		dat[k] = min(dat[(k << 1) + 1], dat[(k << 1) + 2]);
	}
}
int query(int a, int b, int k, int l, int r){
	if(r <= a || b <= l) return INF;
	else if(a <= l && r <= b) return dat[k];
	else{
		int chl = (k << 1) + 1, chr = (k << 1) + 2, m = (l + r) >> 1;
		int vl = query(a, b, chl, l, m);
		int vr = query(a, b, chr, m, r);
		return min(vl, vr);
	}
}
//基于线段树的RMQ结束
int main(){
	while(~scanf("%d%d%d", &N, &M, &E)){
		++M, ++E;
		for(int i = 0; i < N; i++){
			scanf("%d%d%d", &cs[i].s, &cs[i].t, &cs[i].salary);
			++cs[i].s, ++cs[i].t;
		}
		sort(cs, cs + N, cmp);
		//初始化线段树
		for(int i = M; i <= E; i++) dp[i] = INF;
		rmq_init(E + 1);
		dp[M - 1] = 0;
		update(M - 1, 0);
		
		for(int i = 0; i < N; i++){
			int s = cs[i].s, t = cs[i].t, salary = cs[i].salary;
			int v = min(dp[t], query(s - 1, t, 0, 0, n) + salary);
			update(t, v);
			dp[t] = v;
		}
		int res = dp[E];
		if(res == INF) res = -1;
		printf("%d\n", res);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值