三个农民每天清晨 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;
}