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;
}