果园守望者

Description

重庆有很多区县都盛产水果,每当到了水果成熟的时节,何老板就会看准商机,推出了一项名为“果园守望者”的服务。该服务很简单,就是何老板安排人手去为你看守果园,帮你驱赶鸟兽或者小偷。 
最近,何老板又签下了一个看守合同,该合同需要何老板派人看守一个果园,时间是从第A天起一直到第B天。在这B-A+1天里,要求每天至少有一个人在看守果园。 
何老板手下共有N个员工。每个员工都对自己的工作时间和报酬有一定的要求,比如员工甲只在T1..T2这段时间工作,并要求S1元的报酬;员工乙只在T3..T4这段时间工作,并要求S2元的报酬...... 
请你帮助何老板安排一个工作时间表,使得从第A天到第B天每天至少有一个员工在看守果园,并且使得何老板支付的报酬总数尽可能少。

Input

第一行,三个空格间隔的整数N,A,B 
接下来N行,每行三个整数T1,T2,和S,表示对应员工愿意工作的起止时间和要求的报酬。

Output

一个整数,表示何老板需支付的最少报酬总数。如果无法安排员工完成工作,输出-1

Sample Input

3 0 4
0 2 3
3 4 2
0 0 1

Sample Output

5

Hint

1 <= N <= 10,000 
0 <= A <= B <= 90000 
A <= T1 <= T2 <= B 
0 <= S <= 500,000 
样例说明
安排第一个员工工作0,1,2三天 
安排第二个员工工作3,4两天 
就把从第0天到第4天这5天的看守工作完成了。


【分析】

        这道题明显是DP。定义状态数组:F[i]表示以i天结尾的最优值

        如果是不能交叉的覆盖,那么方程为F[T[i].b]=min(F[k])+T[i].w  k<=T[i].a-1;

        但是题目的意思是可以有覆盖的,那么只需要修改边界条件为 T[i].a-1<=k<=T[i].b (因为要完全覆盖,所以有下界)。

        修正后的方程:F[i]=min(F[k])+T[j].w   (i=T[j].b,T[j].a-1<=k<=T[j].b)。但是这样的话复杂度就会很高O(N*B),所以可以用线段树优化。即多加一个域dp来维护动归过程。(我的线段树是堆式线段树,代码有体现)


【代码】

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=1000000000;
int N,A,B;
struct wjj{
	int a,b,w;
}T[10005];
struct node{
	int L,R,dp;
}Tree[400005];
bool _cmp(wjj x,wjj y)
{
	return (x.a<y.a);
}
void _built(int id,int l,int r)
{
	int mid;
	Tree[id].L=l;Tree[id].R=r;Tree[id].dp=INF;
	if(l<r)
	{
		mid=(l+r)>>1;
		_built((id<<1),l,mid);
		_built((id<<1)+1,mid+1,r);
	}
}
void _insert(int id,int r,int v)
{
	if(Tree[id].L==Tree[id].R)
	    Tree[id].dp=min(Tree[id].dp,v);
	else
	{
		int mid=(Tree[id].L+Tree[id].R)>>1;
		if(r<=mid) _insert((id<<1),r,v);
		else _insert((id<<1)+1,r,v);
		Tree[id].dp=min(Tree[id].dp,min(Tree[(id<<1)].dp,Tree[(id<<1)+1].dp));
	}
}
int _findans(int id,int l,int r)
{
	if(l<=Tree[id].L&&Tree[id].R<=r)
	    return Tree[id].dp;
	int mid=(Tree[id].L+Tree[id].R)>>1;
	int temp=INF;
	if(l<=mid) temp=min(temp,_findans((id<<1),l,r));
	if(mid<r) temp=min(temp,_findans((id<<1)+1,l,r));
	return temp;
}
void _init()
{
	scanf("%d%d%d",&N,&A,&B);
	for(int i=1;i<=N;i++)
	    scanf("%d%d%d",&T[i].a,&T[i].b,&T[i].w);
	sort(T+1,T+1+N,_cmp);    //按工作开始时间由小到大排序 
}
void _solve()
{
	int temp,ans;
	_built(1,A,B);
	_insert(1,A-1,0);     //初始化线段树,把A之前的时间所需费用初始化为0 
	for(int i=1;i<=N;i++)
	{
		temp=_findans(1,T[i].a-1,T[i].b);
		_insert(1,T[i].b,temp+T[i].w);
	}
	ans=_findans(1,B,B);
	if(ans==INF)
	    printf("-1\n");
	else
	    printf("%d\n",ans);
}
int main()
{
	_init();
	_solve();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值