7-5 1.2.1 挤牛奶 (80分)

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

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

• 长至少有一人在挤奶的时间段.

• 长的无人挤奶的时间段.

输入格式:
Line 1: 一个整数 N.

Lines 2…N+1: 每行两个小于 1000000 的非负整数,表示一个农民的开始时刻与结束时刻.

输出格式:
一行,两个整数,即题目所要求的两个答案.

输入样例1:
在这里给出一组输入。例如:

3
300 1000
700 1200
1500 2100

输出样例1:
在这里给出相应的输出。例如:

900 300

输入样例2:

10
2 3
4 5
6 7
8 9
10 11
12 13
14 15
16 17
18 19
1 20

输出样例2:

19 0

输入样例3:

3
100 102
64100 64102
128100 128102

输出样例3:

2 63998


【第一次代码】

#include <iostream>
#include <vector>
#include <set>
using namespace std;

int main()
{
    int n;
    cin >> n;
    vector<int> Start(n); // 开始时刻;
    vector<int> End(n);   // 结束时刻;
    for (int i = 0; i < n; i++)
    {
        cin >> Start[i] >> End[i];
    }
    set<int>s_start(Start.begin(), Start.end()); // 利用集合去重并排序
    Start.assign(s_start.begin(), s_start.end());
    set<int>s_end(End.begin(), End.end());
    End.assign(s_end.begin(), s_end.end());

    int big_length_people = End[0] - Start[0]; // 最长有人挤奶段;
    int big_length_no_people = 0;       	   // 最长无人挤奶段;
    int length_people = End[0] - Start[0];     // 有人挤奶的长度;
    int length_no_people = 0;                  // 无人挤奶的长度;
    for (int i = 1; i < n; i++)
    {
        if ((Start[i - 1] < Start[i]) && (Start[i] <= End[i - 1]))// 当前农民的起始时刻位于上一个农民挤奶区间内
        {
            Start[i] = Start[i - 1];
            if (End[i - 1] > End[i])
            {
                End[i] = End[i - 1];
            }
            length_people = End[i] - Start[i];
        }
        else if (Start[i - 1] > Start[i]) // 当前农民起始时刻位于上一个农民起始时刻之前
        {
            if (End[i - 1] > End[i])*斜体样式*
            {
                End[i] = End[i - 1];
            }
            length_people = End[i] - Start[i];
        }
        else if ((Start[i] > End[i-1]))// 当前农民的起始时刻位于上一个农民的结束时刻后,即产生不连续段
        {
            length_people = End[i] - Start[i];
            length_no_people = Start[i] - End[i - 1];
        }
        if (length_people > big_length_people) 	     // 更新最长有人挤奶段
        {
            big_length_people = length_people;
        }
        if (length_no_people > big_length_no_people) //更新最长无人挤奶段
        {
            big_length_no_people = length_no_people;
        }
    }
    cout << big_length_people << " " << big_length_no_people << endl;
    return 0;
}

【错因】

这里虽然用到集合set去重并排序,但没有考虑到开始时刻与结束时刻的对应关系,导致去重并排序时破坏了时刻的对应关系。

【改进】
使用容器map是个好想法:
(1)map的键值对是一一对应关系,和开始时刻与结束时刻的一一对应关系相同,可把“开始时刻”作“key”,“结束时刻”作“value”,保护了一一对应关系;

(2)且map的键是唯一的,相同的“开始时刻”即相同的“key”只保留一个,而把对应的最大的“结束时刻”作为“value”保留,可以达到去重效果;

(3)还有map自动根据“key”的大小进行排序,可以达到对“开始时刻”进行排序效果;


【源代码】

#include <iostream>
#include <vector>
#include <map>
using namespace std;

int main()
{
    int n;
    cin >> n;
    map<int, int> m;
    unsigned int start, end; // 开始时刻,结束时刻;
    for (int i = 0; i < n; i++)
    {
        cin >> start >> end;
        if (m.find(start) == m.end()) // 当前的start不存在map的key中,此时start可作为新key存入;
        {
            m[start] = end; // 将当前的“开始时刻-结束时刻”作为“key-value对”存入map中;
        }
        else // 若当前的“start”存在map中的key中,则比较当前“end”与“map[start]”的大小,取两者较大的作为结束时刻存入map中,保证连续时刻的长度最大;
        {
            if (m[start] < end)
            {
                m[start] = end;
            }
        }
    }
    int s = m.size(); //获取字典的大小:有几个元素;
    vector<int> Start(s);
    vector<int> End(s);

    map<int, int>::iterator   iter; // 前向迭代器
    int i = 0;

    for (iter = m.begin(); iter != m.end(); iter++) // 将key和value分别按顺序存入两个数组中,保证一一对应关系;
    {
        Start[i] = iter->first;
        End[i] = iter->second;
        i++;
    }

    int iMax_pe = End[0] - Start[0]; // 最长有人挤奶段;
    int iMax_no = 0;       			 // 最长无人挤奶段;
    int l_pe = End[0] - Start[0];    // 有人挤奶的长度;
    int l_no = 0; 					 // 无人挤奶的长度;
    
    for (i = 1; i < s; i++)
    {
        if ((Start[i - 1] < Start[i]) && (Start[i] <= End[i - 1])) // 当前农民的起始时刻位于上一个农民挤奶区间内,详情见附图Condition 1
        {
            Start[i] = Start[i - 1];
            if (End[i - 1] > End[i])
            {
                End[i] = End[i - 1];
            }
            l_pe = End[i] - Start[i];
        }
        else if (Start[i] > End[i-1]) // 当前农民的起始时刻位于上一个农民的结束时刻后,产生不连续段,详情见附图Condition 2
        {
            l_pe = End[i] - Start[i];
            l_no = Start[i] - End[i - 1];
        }
        
        if (l_pe > iMax_pe)	// 更新最长有人挤奶段
        {
            iMax_pe = l_pe;
        }
        if (l_no > iMax_no)	//更新最长无人挤奶段
        {
            iMax_no = l_no;
        }
    }
    cout << iMax_pe << " " << iMax_no << endl;
    return 0;
}

附图

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值