zoukankan      html  css  js  c++  java
  • P2234 [HNOI2002]营业额统计题解

    题目传送门

    一、双链表解法

    1、需要记录原始数据(a[i]),索引(i):第几天,值:多少钱。
    2、为了按金额排序,同时还需要记录相应的第几天,所以引入了结构体。
    3、按结构体排序后的结果组成了一个双链表,本质上就是按金额从小到大排序的链,而链表中保存的数据是天数,注意,是天数,而不是金额。就是哪一天排在哪一天后面。
    4、倒着查找是本思路的精华。先从最后一天开始,根据上面的双链表,找出它的前驱和后继都是哪天,因为它是最后的一天,所以可以理解为找出的前驱和后继都是它前面的数字,这样,就可以直接计算了。
    5、找出最后一天的最小波动值后,最后一天就没有存在的必要了,把它从链表中清除掉,防止后续的天计算时再错误的找到它。

    6、总结一下本题的数据结构
    (1)原始数据数组 (a[N]),描述:根据第几天,获取当天多少钱。
    (2)结构体数组(s[N]),描述:按金额从小到大排序的数组,值为第几天。
    (3)双链表,其实是对结构体数组的一个变形,因为如果直接使用结构体数组的话,有两个问题,一是不方便直接找出前驱和后继结点,二是不方便删除操作。有了双链表,这两个操作就方便多了。说白了,就是双链表用于维护动态数据很牛X,静态数组这方面不给力。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 32768;
    int ans;
    
    //结构体:某一天
    //money:营业额,day:第day天
    struct node {
        int money;
        int day;
    } s[N];
    
    //结构体的比较函数,以钱小在前,钱大在后进行排序
    bool cmp(node x, node y) {
        return x.money < y.money;
    }
    
    //双链表的声明
    int n, nex[N], pre[N], a[N];
    
    int main() {
        //输入
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];            //原始数组,第1,2,3 ...天的营业额是多少
            s[i] = {a[i], i};       //为了按营业额排序,但还需要保留是哪天的信息,没办法,只能使用结构体
        }
        //排序
        sort(s + 1, s + 1 + n, cmp);//按金额由小到大排序
    
        //"动态查找前驱后继",可以用链表的离线做法
        //创建结构体+双链表,按金额排序后,日期的前后关系。
        for (int i = 1; i <= n; i++) {
            nex[s[i].day] = s[i + 1].day;
            pre[s[i].day] = s[i - 1].day;
        }
    
        //从后向前,这个非常棒!这样的话,就可以无视前驱和后继的概念,不管是存在前驱还是后继,其实都是前面的日子金额,全可以用。
        //用后最删除,不影响其它日期判断
        for (int i = n; i >= 1; i--) {
            //没有后继,有前驱
            if (nex[i] == 0 && pre[i] > 0)ans += abs(a[i] - a[pre[i]]);
                //没有前驱,有后继
            else if (nex[i] > 0 && pre[i] == 0) ans += abs(a[i] - a[nex[i]]);
                //有前驱有后继
            else if (pre[i] > 0 && nex[i] > 0)ans += min(abs(a[nex[i]] - a[i]), abs(a[pre[i]] - a[i]));
                //无前驱,无后继
            else if (pre[i] == 0 && nex[i] == 0)ans += a[i];
            //删除当前结点,防止前面的节点错误的找到它
            nex[pre[i]] = nex[i];
            pre[nex[i]] = pre[i];
        }
    
        //输出
        cout << ans << endl;
        return 0;
    }
    

    二、STL解法

    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF = 0x3f3f3f3f;
    set<int> s;//set 自带排序功能,强!set本身就是一种有序的容器。
    int n, x, ans;
    
    int main() {
        //set中放入极大和极小值,这个放入极大与极小值的技巧太棒了,可以有效避免越界等问题,值得背诵~
        s.insert(INF);
        s.insert(-INF);
        //读入
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> x;
            //如果set是空,只有极大与极小值时.也就是第一个
            if (s.size() == 2) {
                ans += x;
                s.insert(x);
            } else {
                //set居然支持二分法搜索~太棒了
                //lower_bound() 函数用于在指定区域内查找不小于目标值的第一个元素
                auto k = s.lower_bound(x);
                //如果不相等时(相等就是零了,不需要加入)
                if (*k != x) {
                    //复制出来迭代器
                    auto a = k;
                    //前驱
                    a--;
                    //最小波动值和
                    ans += min(abs(*a - x), abs(*k - x));
                    //金额入集合
                    s.insert(x);
                }
            }
        }
        //输出结果
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    BZOJ 1093: [ZJOI2007]最大半连通子图
    BZOJ 1406: [AHOI2007]密码箱
    BZOJ 1073: [SCOI2007]kshort
    BZOJ 1857: [Scoi2010]传送带
    AC日记——天天爱跑步 洛谷 P1600
    AC日记——[Sdoi2010]粟粟的书架 bzoj 1926
    AC日记——The Shortest Path in Nya Graph hdu 4725
    AC日记——文化之旅 洛谷 P1078
    AC日记——【模板】分块/带修改莫队(数颜色) 洛谷 P1903
    AC日记——大爷的字符串题 洛谷 P3709
  • 原文地址:https://www.cnblogs.com/littlehb/p/15077940.html
Copyright © 2011-2022 走看看