题解 洛谷P4644 【[Usaco2005 Dec]Cleaning Shifts 清理牛棚】

题面:

约翰的奶牛们从小娇生惯养,她们无法容忍牛棚里的任何脏东西。约翰发现,如果要使这群有洁癖的奶牛满意,他不得不雇佣她们中的一些来清扫牛棚,约翰的奶牛中有 N(1N10000) 头愿意通过清扫牛棚来挣一些零花钱。

由于在某个时段中奶牛们会在牛棚里随时随地地乱扔垃圾,自然地,她们要求在这段时间里,无论什么时候至少要有一头奶牛正在打扫。需要打扫的时段从某一天的第 M 秒开始,到第 E 秒结束(0ME86399)。注意这里的秒是指时间段而不是时间点,也就是说,每天需要打扫的总时间是 EM+1 秒。

约翰已经从每头牛那里得到了她们愿意接受的工作计划:对于某一头牛,她每天都愿意在笫 T1T2 秒的时间段内工作 ((MT1T2E) ,所要求的报酬是 S 美元 (0S500000)。与需打扫时段的描述一样,如果一头奶牛愿意工作的时段是每天的第 1020 秒,那她总共工作的时间是 11 秒,而不是 10 秒。

约翰一旦决定雇佣某一头奶牛,就必须付给她全额的工资,而不能只让她工作一段时间,然后再按这段时间在她愿意工作的总时间中所占的百分比来决定她的工资。现在请你帮约翰决定该雇佣哪些奶牛以保持牛棚的清洁,当然,在能让奶牛们满意的前提下,约翰希望使总花费尽量小。

输入格式

第 1 行: 3 个正整数 N,M,EN,M,E 。

第 2 到 N+1 行:第 i+1 行给出了编号为 ii 的奶牛的工作计划,即 3 个正整数 T1,T2,S 。

输出格式

输出一个整数,表示约翰需要为牛棚清理工作支付的最少费用。如果清理工作不可能完成,那么输出 -1


 

这道题本身只是一道比较水的dp,但是……它会卡O(n^2)的算法!!!

所以,我们可以用数据结构优化!我用的线段树(单修区查多好写呀)

要注意几点:
1.dp数组在起点要清零
2.循环取最小值时是从t[i].l-1到t[i].r
3.线段树minn要取最大
4.区间排序按右端点排


先上70分代码(我先得了100分为了题解再亲测了一遍70……)

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
const int INF=0x3f3f3f3f,MAXX=100000;
int n,l,r;
int cnt;
int dp[MAXX+5];
struct node{int l,r,v;}t[MAXX+5];
bool cmp(node x,node y){return x.r<y.r;}//按右端点从小到大排序 
int main()
{
    scanf("%d %d %d",&n,&l,&r);
    for(int i=1;i<=n;i++)
    {
        int x,y,z;
        scanf("%d %d %d",&x,&y,&z);
        if(x<l)x=l;if(y>r)y=r;//不知道有没有,但是为了以防万一还是写上了 
        if(x>y)continue;//同上 
        t[++cnt].l=x;t[cnt].r=y;t[cnt].v=z;
    }
    sort(t+1,t+cnt+1,cmp);
    memset(dp,0x3f,sizeof(dp));//dp数组清最大值 
    dp[l]=0;//左端点初值 
    for(int i=1;i<=cnt;i++)
    {
        int k=INF;
        for(int j=t[i].l-1;j<=t[i].r;j++)k=min(k,dp[j]);//从t[i].l-1——t[i].r 
        dp[t[i].r]=min(dp[t[i].r],k+t[i].v);//更新 
    }
    if(dp[r]==INF)printf("-1");//如果最后值为INF说明中间必定有时间打扫不到,输出-1 
    else printf("%d",dp[r]);
    return 0;
}

  


满分代码

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
const int INF=0x3f3f3f3f,MAXX=100000;
int delta=10;
int n,l,r;
int cnt;
int dp[MAXX+5];
struct tree{int l,r,minn;}tree[MAXX*8+5];
struct node{int l,r,v;}t[MAXX+5];
bool cmp(node x,node y){return x.r<y.r;}//按右端点从小到大排序 
void Build(int k,int l,int r)
{
    tree[k].l=l;tree[k].r=r;tree[k].minn=INF;//线段树最小值清最大 
    if(l==r){return;}
    int mid=(l+r)>>1;
    Build(k<<1,l,mid);
    Build(k<<1|1,mid+1,r);
}
void add(int k,int x,int v)
{
    if(x<tree[k].l||x>tree[k].r)return;
    tree[k].minn=min(tree[k].minn,v);
    int mid=(tree[k].l+tree[k].r)>>1;
    add(k<<1,x,v);add(k<<1|1,x,v);
}
int ask(int k,int l,int r)
{
    int ans=INF;
    if(l<=tree[k].l&&r>=tree[k].r)return tree[k].minn;
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid)ans=min(ans,ask(k<<1,l,r));
    if(r>mid)ans=min(ans,ask(k<<1|1,l,r));
    return ans;
}//模版线段树单修区查 
int main()
{
    Build(1,1,100000);//按数据范围建树 
    scanf("%d %d %d",&n,&l,&r);
    l+=10;r+=10;//全部加一个小数,不然会玄学RE
    for(int i=1;i<=n;i++)
    {
        int x,y,z;
        scanf("%d %d %d",&x,&y,&z);
        x+=10;y+=10;
        if(x<l)x=l;if(y>r)y=r;//不知道有没有,但是为了以防万一还是写上了 
        if(x>y)continue;//同上 
        t[++cnt].l=x;t[cnt].r=y;t[cnt].v=z;
    }
    sort(t+1,t+cnt+1,cmp);
    memset(dp,0x3f,sizeof(dp));//dp数组清最大值 
    dp[l]=0;//左端点初值 
    add(1,l,0);//更新到线段树里 
    for(int i=1;i<=cnt;i++)
    {
        int k=INF;
        k=min(k,ask(1,t[i].l-1,t[i].r));//从t[i].l-1——t[i].r 
//      cout<<1;
        dp[t[i].r]=min(dp[t[i].r],k+t[i].v);//更新的同时更新到线段树里 
        add(1,t[i].r,dp[t[i].r]);
//      cout<<2;
    }
    if(dp[r]==INF)printf("-1");//如果最后值为INF说明中间必定有时间打扫不到,输出-1 
    else printf("%d",dp[r]);
    return 0;
}

  

posted @ 2019-08-06 11:55  Peter_Rabbit  阅读(231)  评论(0编辑  收藏  举报