• NOIP2011 D2T3 观光公交 做题笔记


    归纳题目的性质

    每一个加速器效果相同(1)

    车子等到所有人上车之后才会发车, 这个最早发车时间不由加速器的配比决定(2)

    要优化的对象: "所有人搭车时间总和". (3)

    算法

    根据性质(2), 我们可以预处理出这个"最早发车时间", 记为(start_i)

    60分

    根据(2), (3)设计算法, 想到DP.

    令站点i, 使用加速器个数为j的最晚发车时间为(dp_{i, j})(蕴含性质1), 则可以由(dp_{i, j} ightarrow dp_{i+1, k}(k>j)), 其中贡献可以预处理(思考性质3)

    复杂度(O(nk^2))

    100分

    思考贪心: 把整个列车系统看成一个整体(前一个算法是把每一站分拆), 将每个加速器的贡献拆开. (性质1)

    思考加速器的性质 : 在每一站使用都相同(令其为性质4), 考虑枚举在当前状态下哪一站加速最佳.(在(O(n))内)

    定义如下符号:

    [rb_i: 加速器设在第i站的影响范围(先不解释)\ tim_i: 在当前状态下, 汽车到达i站最佳时间\ l_i: i ightarrow i+1 时间 ]

    这个算法的关键是(rb_i)数组, 请看定义, 不理解没关系

    [rb_i = i+1 ext{if not ok_to_opt}\ ext{else } rb_i = rb_{i+1} ]

    如果在第i站至i+1站的路程中使用加速器, 目的地(in[i+1,rb_i]) 的人这些人消耗时间会-1s:
    为什么?

    把目的地(in[i+1,rb_i]) 的人分为2类:

    1. 出发地(leq)i.
    2. 出发地(>)i

    第1类人显然消耗时间会减少1s
    第2类人虽然在(i ightarrow i+1)的路程中没有减少消耗时间, 但是由于发车时间提前了1s(根据(rb_i)的定义), 他们消耗时间也会减少1s (这个我一开始没有想到)....

    而那些目的地(>rb_i)的人来说, 发车时间太晚, (tim_{rb_{i+1}})的减小无济于事...

    经过k次迭代我们就做完啦(滑稽)
    complexity (O(nk)), it's unexpected but it works(and also fast)!

    code

    #include<bits/stdc++.h>
    using namespace std;
    
    int read() {
        int f = 1, ans = 0; char c = getchar(); 
        while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
        while(isdigit(c)) {ans = ans * 10 + c - '0', c = getchar();}
        return ans * f;
    }
    #define rep(i, _st, _ed) for(register int i = (_st); i <= (_ed); ++i)
    const int maxm = 10005, maxn = 1005, maxk = 1e5+5;
    int leave[maxn], dis[maxn], endp[maxn], rg[maxn], t[maxn]; //range是关键字
    int st[maxm], ed[maxm], arri[maxm];
    int n, m, k, ans;
    
    void test() { int tmp = read(); printf("%d
    ", tmp);}
    void put(int x) {printf("%d
    ", x);} 
    signed main(){
    //	while(1)test();
        scanf("%d %d %d", &n, &m, &k);
        rep(i, 1, n-1) dis[i] = read();
        rep(i, 1, m) arri[i] = read(), st[i] = read(), ed[i] = read();
        rep(i, 1, m) {
            leave[st[i]] = max(leave[st[i]], arri[i]);
            endp[ed[i]]++;
        }
        rep(i, 2, n) endp[i] += endp[i-1]; // endp[i] is prefix sum
        ans = 0;
        rep(i, 1, n) t[i] = max(leave[i-1], t[i-1]) + dis[i-1]; //, put(t[i]);
        rep(i, 1, m) ans = ans + t[ed[i]] - arri[i];
        //errorcode : ans = ans + t[ed[i]] - t[st[i]]; 
        //一些预处理工作
    //	put(ans); 
        while(k--){
            rg[n] = n;
            for(int i = n-1; i >= 1; --i) {
                if(t[i+1] > leave[i+1]) rg[i] = rg[i+1];
                else rg[i] = i + 1;
    //            put(rg[i]);
            }
    //        put(0);
            int opt = 0, pos = 0;
            rep(i, 1, n-1) {
                if(dis[i] > 0 && endp[rg[i]] - endp[i] > opt) {
    				opt = endp[rg[i]] - endp[i];
    				pos = i;
    			}
            }
            if(pos==0) break;
            ans -= opt;
            --dis[pos]; //don't forget to update the distance!! 
            rep(i, pos, n) t[i] = max(leave[i-1], t[i-1]) + dis[i-1]; 
            //don't forget to update the arrival time!!
        }
        put(ans);
        return 0;
    }
    

    大家来找茬

    这是我的一个错误版本代码, 有5处bug..

    #include<bits/stdc++.h>
    using namespace std;
    
    // #pragma G++ optimize("O3")
    
    int read() {
        int f = 1, ans = 0; char c = getchar(); 
        while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
        while(isdigit(c)) {ans = ans * 10 + c - '0', c = getchar();}
        return ans * f;
    }
    #define rep(i, _st, _ed) for(register int i = (_st); i <= (_ed); ++i)
    const int maxm = 10005, maxn = 1005, maxk = 1e5+5;
    int leave[maxn], dis[maxn], endp[maxn], rg[maxn], t[maxn]; //range是关键字
    //t[i]为从第i站出发的时间
    int st[maxm], ed[maxm], arri[maxm];
    vector<int> d_set[maxn]; //从第i站出发人员集合
    int n, m, k, ans;
    void test() { int tmp = read(); printf("%d
    ", tmp);}
    signed main(){
    	test();
        scanf("%d %d %d", &n, &m, &k);
        rep(i, 1, n-1) dis[i] = read();
        rep(i, 1, m) arri[i] = read(), st[i] = read(), ed[i] = read();
        rep(i, 1, m) {
            leave[ed[i]] = max(leave[ed[i]], arri[i]);
            d_set[st[i]].push_back(ed[i]);
            endp[ed[i]]++;
        }
        ans = 0;
        rep(i, 2, n) endp[i] += endp[i-1]; // endp[i] is prefix sum    
        rep(i, 1, n) t[i] = max(leave[i], t[i-1] + dis[i-1]);
        rep(i, 1, m) ans = ans + t[ed[i]] - arri[i];
        //errorcode : ans = ans + t[ed[i]] - t[st[i]]; 
        //一些预处理工作
    
        while(k--){
            rg[n] = n;
            for(int i = n-1; i >= 1; --i) 
                if(t[i] + dis[i] > leave[i+1]) rg[i] = rg[i+1];
                else rg[i] = i + 1;
            int opt = 0;
            rep(i, 1, n) {
                if(dis[i] > 0 && endp[rg[i]] - endp[i] > opt) {
    				opt = endp[rg[i]] - endp[i];
    			}
            }
            ans -= opt;
        }
        printf("%d
    ", ans);
        return 0;
    }
    

    总结

    这道题的结论很优美, 但是优美地不像是可以考试时想出来的...
    还是写dp保平安吧...
    p.s.这道题对思维缜密性的要求很高
    参考: https://blog.csdn.net/qq_37220238/article/details/80059161
    https://www.cnblogs.com/pealicx/p/6138785.html(code)

  • 相关阅读:
    小程序-文章:微信小程序常见的UI框架/组件库总结
    小程序-文章:微信第三方登录(静默授权和非静默授权)
    asterisk
    Java实现 洛谷 P1423 小玉在游泳
    Java实现 洛谷 P1423 小玉在游泳
    Java实现 洛谷 P1423 小玉在游泳
    Java实现 洛谷 P1035 级数求和
    Java实现 洛谷 P1035 级数求和
    Java实现 洛谷 P1035 级数求和
    Java实现 洛谷 P1035 级数求和
  • 原文地址:https://www.cnblogs.com/Eroad/p/9757811.html
Copyright © 2020-2023  润新知