• [Usaco2005 Dec]Cleaning Shifts


    [Usaco2005 Dec]Cleaning Shifts

    给出n段区间,左右端点分别为(l_i,r_i),以及选取这段区间的费用(c_i),现在要选出若干个区间,使其完全覆盖区间([m,e]),询问费用之和的最小值,(1≤n≤10000,0≤m≤e≤86399)

    法一:

    不妨把区间按左端点排序,如果在大区间范围外,可以筛除,虽然题目有保障,于是设(f_i)表示以第i个区间结尾,覆盖第i个区间前所有需要覆盖的位置的最少代价,于是有

    [f_i=min_{j=1,r_j+1geq l_i}^{i-1}{f_j}+1 ]

    边界:把覆盖大区间左端点全部手动初始化,其余无限大

    答案:覆盖了大区间右端点的(f_i)

    注意到这实际上是(O(n^2))算法,但是因为n比较小,实际上这是(C_n^2),其实没有(10^8),于是可以水过。

    参考代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define il inline
    #define ri register
    #define intmax 16843009
    using namespace std;
    struct interval{
        int l,r,c;
        il bool operator<(const interval&x)const{
            return l<x.l;
        }
    }I[10001];
    int dp[10001];
    int main(){
        int n,m,e;scanf("%d%d%d",&n,&m,&e);
        for(int i(1);i<=n;++i)
            scanf("%d%d%d",&I[i].l,&I[i].r,&I[i].c);
        sort(I+1,I+n+1),memset(dp,1,sizeof(dp));
        ri int i,j;
        for(i=1;i<=n;++i)
            if(I[i].l==m)dp[i]=I[i].c;
            else break;
        while(i<=n){
            for(j=i-1;j;--j)
                if(I[j].r+1>=I[i].l)
                    if(dp[j]<dp[i])dp[i]=dp[j];
            dp[i]+=I[i].c,++i;
        }int ans(intmax);
        for(i=n;i;--i)
            if(I[i].r==e)
                if(ans>dp[i])ans=dp[i];
        if(ans<intmax)printf("%d",ans);
        else puts("-1");
        return 0;
    }
    
    

    法二:

    注意到只要出题人稍微开大数据范围,就game over了,有水过的痕迹,而且转移也不支持优化,我们只能换状态了,注意到如果法一是正解的,m,e其实可以开到long long范围,但是m,e很小,进入(nlog^n)范围,所以意识到可以以位置为状态。

    因此设(f_j)为覆盖m到j位置的区间的最小费用,区间按右端点排序,枚举区间i,于是不难有

    [f_j=min_{l_i-1leq kleq r_i-1}{f_{k}}+c_i ]

    边界:(f_{m-1}=0),其余无限大

    答案:类似法一

    注意到每次我们实际上查询的已求出的f中的某一段的最小值,而已求出的不会被更新,因此我们需要区间查询,于是线段树或者树状数组可以将之优化为(O(nlog^n))

    参考代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define il inline
    #define ri register
    #define intmax 16843009
    using namespace std;
    il int min(int a,int b){
        return a<b?a:b;	
    }
    struct interval{
        int l,r,c;
        il bool operator<(const interval&x)const{
            return r<x.r;
        }
        il void read(){
            scanf("%d%d%d",&l,&r,&c),++l,++r;
        }
    }I[10001];
    struct segment_tree{
        int a[86400];
        struct data{
            int l,r,d;
        }t[345600];
        il void build(int p,int l,int r){
            t[p].l=l,t[p].r=r;
            if(l==r)return (void)(t[p].d=a[l]);
            int mid(l+r>>1),pl(p<<1),pr(pl|1);
            build(pl,l,mid),build(pr,mid+1,r);
            t[p].d=min(t[pl].d,t[pr].d);
        }
        il void change(int p,int x,int v){
            if(t[p].l==t[p].r)return (void)(t[p].d=v);
            int mid(t[p].l+t[p].r>>1),pl(p<<1),pr(pl|1);
            if(x<=mid)change(pl,x,v);if(x>mid)change(pr,x,v);
            t[p].d=min(t[pl].d,t[pr].d);
        }
        il int ask(int p,int l,int r){
            if(l<=t[p].l&&t[p].r<=r)return t[p].d;
            int mid(t[p].l+t[p].r>>1),pl(p<<1),pr(pl|1),ans(intmax);
            if(l<=mid)ans=min(ans,ask(pl,l,r));
            if(r>mid)ans=min(ans,ask(pr,l,r));
            return ans;
        }
    }T;
    int main(){
        int n,m,e,i;
        scanf("%d%d%d",&n,&m,&e),++m,++e;
        for(i=1;i<=n;++i)I[i].read();
        sort(I+1,I+n+1),memset(T.a,66,sizeof(T.a));;
        T.a[m-1]=0,T.build(1,0,e);
        for(i=1;i<=n;++i)
            T.change(1,I[i].r,min(T.ask(1,I[i].l-1,I[i].r)+I[i].c,T.ask(1,I[i].r,I[i].r)));
        int ans(T.ask(1,e,e));
        if(ans>=intmax)puts("-1");
        else printf("%d",ans);
        return 0;
    }
    
    

    法三:双平衡树

    首先鸣谢lsy神犇,提供了这种神奇的思路,优化了法一。

    是否还记得法一的(O(n^2))水过?如果我们把m,e变成long long范围,那么你就不得不离散化,这样也就很难写,容易出错,但是除了对位置的考虑,我们还可以对区间尽心考虑,于是我们为了排除无效的决策,把决策集合中的r和dp放入set a,这样当它的r不满足条件,就很容易删除了,但是要查最大的dp值,我们可以把dp单独放入set b,而当r被删除,在set b中找这个dp值,把它删去即可。

    参考代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <set>
    #include <algorithm>
    #define il inline
    #define ri register
    #define intmax 16843009
    using namespace std;
    struct pi{
        int x,y;
        il bool operator<(const pi&a)const{
            return x<a.x;
        }
    };
    multiset<pi>A;
    multiset<int>B;
    struct interval{
        int l,r,c;
        il bool operator<(const interval&x)const{
            return l<x.l;
        }
    }I[10001];
    int dp[10001];
    int main(){
        int n,m,e;scanf("%d%d%d",&n,&m,&e);
        for(int i(1);i<=n;++i)
            scanf("%d%d%d",&I[i].l,&I[i].r,&I[i].c);
        sort(I+1,I+n+1),memset(dp,1,sizeof(dp));
        ri int i;
        for(i=1;i<=n;++i)
            if(I[i].l==m){
                dp[i]=I[i].c,B.insert(dp[i]);
                A.insert((pi){I[i].r,dp[i]});
            }else break;
        while(i<=n){
            while(A.size()&&A.begin()->x+1<I[i].l)
                B.erase(B.find(A.begin()->y)),A.erase(A.begin());
            if(B.size())dp[i]=*B.begin()+I[i].c;
            A.insert((pi){I[i].r,dp[i]}),B.insert(dp[i]),++i;
        }int ans(intmax);
        for(i=n;i;--i)if(I[i].r==e)if(ans>dp[i])ans=dp[i];
        if(ans<intmax)printf("%d",ans);else puts("-1");
        return 0;
    }
    
  • 相关阅读:
    逆序数
    Java处理对象
    Java8增强的包装类
    Java初始化块
    Linux- Linux软件配置
    Python- 【python无法更新pip】提示python.exe: No module named pip
    Error- Overloaded method value createDirectStream in error Spark Streaming打包报错
    Error- spark streaming 打包将全部依赖打进去Invalid signature file digest for Manifest main attributes
    Spark- Spark从SFTP中读取zip压缩文件数据做计算
    JAVA- 内部类及匿名内部类
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/10958338.html
Copyright © 2020-2023  润新知