• USACO / Milking Cows (离散化,哈希,线段树,动规)


     

     

    Milking Cows 挤牛奶


    描述

    三个农民每天清晨5点起床,然后去牛棚给3头牛挤奶。第一个农民在300秒(从5点开始计时)给他的牛挤奶,一直到1000秒。第二个农民在700秒开始,在 1200秒结束。第三个农民在1500秒开始2100秒结束。期间最长的至少有一个农民在挤奶的连续时间为900秒(从300秒到1200秒),而最长的无人挤奶的连续时间(从挤奶开始一直到挤奶结束)为300秒(从1200秒到1500秒)。

    你的任务是编一个程序,读入一个有N个农民(1 <= N <= 5000)挤N头牛的工作时间列表,计算以下两点(均以秒为单位):

    • 最长至少有一人在挤奶的时间段。
    • 最长的无人挤奶的时间段。(从有人挤奶开始算起)

    PROGRAM NAME: milk2

    INPUT FORMAT:

    (file milk2.in)

    Line 1:

    一个整数N。

    Lines 2..N+1:

    每行两个小于1000000的非负整数,表示一个农民的开始时刻与结束时刻。

    OUTPUT FORMAT:

    (file milk2.out)

    一行,两个整数,即题目所要求的两个答案。


    SAMPLE INPUT

    3
    300 1000
    700 1200
    1500 2100
    


    SAMPLE OUTPUT

    900 300

    USACO/milk2

    有四种思想分析

     

    离散化

    (其实就是进行了优化的搜索而已)

    按照开始时间升序排序,然后从左到右扫一遍,复杂度是O(nlogn+n)的(排序+扫一遍,用堆、合并、快排都可以)。

    所谓从左到右扫一遍,就是记录一个当前区间,[tmp_begin , tmp_end]

    如果下一组数据的begin比tmp_end的小(或相等),则是连接起来的,检查这组数据的end,取max{end , tmp_end}。

    如果下一组数据的begin比tmp_end的大,则是相互断开的,整理本区间,ans1取max{tmp_end - tmp_begin , ans1}。ans2取max{begin - tmp_end , ans2}

     

    /* ID: 138_3531 PROG: milk2 LANG: C++ */ #include <iostream> #include <fstream> #include <algorithm> int MAX(int a,int b) {     return a>b?a:b; } using namespace std; struct Time {     int begin;     int end; }; bool cmp(const Time & a,const Time & b) {     return a.begin<b.begin; } int main() {     ofstream fout ("milk2.out");     ifstream fin ("milk2.in");     int n;     Time f[5001];     fin>>n;     for (int i=0;i<n;i++)        fin>>f[i].begin>>f[i].end;     sort(f,f+n,cmp);     int tmpbegin=f[0].begin,tmpend=f[0].end;     int ans1=tmpend-tmpbegin,ans2=0;     for (int i=1;i<n;i++)     {         if (f[i].begin<=tmpend)             tmpend=MAX(tmpend,f[i].end);         else         {             ans1=MAX(ans1,tmpend-tmpbegin);             ans2=MAX(ans2,f[i].begin-tmpend);             tmpbegin=f[i].begin;             tmpend=f[i].end;         }     }     fout<<ans1<<' '<<ans2<<endl;     return 0; }

     

    ++++++++++++++++++++++++++++

    线段树

    本题的规模是1e6,简单的模拟是O(nm)(n是奶牛个数,m是最大范围)的,会超时。(但是本题数据远没有描述的那么恐怖,直接模拟也是很快的}

    用线段树统计区间,复杂度降为O(nlogm+m),可以接受。

     

    怀疑是线段树的变形?

    把每个输入看作是一个所谓的“线段”,只有开头和结尾。

    既然每条线段有重叠现象,马上想出可以合并!按照开头和结尾从小到大排序,for一遍,如果发现前后两条线段开头与结尾重叠了,

    (当然也要考虑被完全包括的情况),马上修改开头与结尾,并删除(可以标记),最后在剩下的线段中统计就可以了。

    (好像非常易懂,也很容易实现)

     

    标记数组(哈希)

    1e6的范围,开一个布尔数组完全可以,有人为TRUE,无人为FALSE,注意边界即可。最后线性扫描即可。

    时间复杂度,应该是O(n),n为最后结束的时间。

    缺点就是……比较慢

    +++++++++++本是同算法,相割何太急++++++++++++

    和我的方法比较像,但我做了些优化

    预处理:

     建立一个数组a。
     读入,若为起点将a[i]加1,若为终点将a[i]减1。
     (这时顺便找出总的起点与终点);
    

    算法开始:

     将数组扫一遍(注意从总的起点扫到总的终点),这时将x(初始为0)加上a[i]。
      若遇到x由0变1,或由1变0,
      将这个点计入数组ans[]。
      然后再将ans扫描一遍,大家可能都想到了:
       若i为奇数,a[i+1]-a[i] 应该是有人的时间间隔;
       若i为偶数,反之。
    
    这个算法是O(n),实际效果不错,但我也不知道应该叫什么。
    


    叫什么好呢?并查集加速直接模拟

    记录一个fa[i]表示i之后第一个没覆盖的点。 下次遇到这里的时候就可以直接跳过了。 复杂度大概算o(n)吧。

     


    分段动规


    消逝的分割线-------------------------
           时间复杂度(nlogn),全部0毫秒。 以开始时间从小到大快排每个农民

    快排后:

    f[i]表示第i个农民所在的最长连续线段 last_start表示这个线段的起点。 a[i].begin 第i个农民的起点时间,a[i].end 终点时间

    可以得出方程: f[i]={max{f[i-1],a[i].end} (a[i].begin<=f[i-1]) //加上第i个农民仍然连续

         a[i].end             (a[i].begin>f[i-1])  /*加上第i个农民变不连续 
                                                     在这里开始以此农民的开始时间的新的一条连续线段,
                                                     更新last_start=a[i].begin作为新起点,因为有间隔,
                                                     所以更新longest_idle_time=max(longest_idle_time,a[i].begin-f[i-1]);*/
    

    在每次循环处理后longest_continuous_time=max(longest_continuous_time,f[i]-last_start) 最后输出longest_continuous_time和longest_idle_time即可。

     
  • 相关阅读:
    Centos查看tomcat状态及操作
    python实现文件下载
    python深浅copy
    linux下jenkins实现自动化部署
    Linux下Java的安装
    通过filezilla将windows文件上传到linux中
    jmeter性能测试实现分布式
    性能测试-JMeter ServerAgent服务器资源监控
    vscode折叠/展开所有区域代码快捷键
    Linux的tar压缩解压缩命令
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/2598286.html
Copyright © 2020-2023  润新知