问题:
三个农民在5点起床,去畜棚帮3头奶牛挤奶。第一个农民在第300秒(从早上五点开始算起)开始挤他的奶牛,并在第1000秒结束。第二农民开始时间为700,结束为1200。第三个农民开始为1500,结束为2000。至少有一个农民在挤奶的最长的持续时间为900(从300到1200)。没有农民挤奶的最长持续时间为300(1500减去1200)。
写一个程序通过计算N的农民挤N头奶牛的开始和结束时间的列表(以秒计算),获得以下结果:
- 持续最长的至少有1个农民在挤奶的时间间隔。
- 没有农民挤奶的最长持续时间(在挤奶开始之后)。
程序名:milk2
输入格式
行 1: |
一个整数 |
行 2..N+1: |
T小于1000000的正整数, 在0500之后以秒计算的开始和结束时间。 |
分析:
农民1的时间区间为:[300,1000]
农民2的时间区间为:[700,1200]
农民3的时间区间为:[1500,2000]
合并区间可以得到[300,1200],[1500,2000]
显然合并后区间的长度中最大值便是我们所求的最长持续时间。这里是max{900,500}
所以区间的两两间的间隔中最大值便是我们所求的最长中断时间。这里是max{300}
所以我们只有获得一个合并后的区间,再求以上两个值的集合后取相应的最大值就可以得到本题的解。
农民的区间排列并非有序,农民2的区间可以是[100,500]。所以首先我们需要对区间左端进行升序排序。
对区间的右端排序之后,对于相邻的两个区间[a(n),b(n)]与[a(n+1),b(n+1)],由于a(n)<=a(n+1)(已知a(n)<=b(n)),
故这两个区间的关系只有包含,相交,以及无交集三个关系。可以得到
包含:a(n+1)<=b(n) b(n+1)<=b(n)
相交:a(n+1)<=b(n)
无交集:a(n+1)>b(n)
遍历两次依据这三种关系,做适当操作即可将合并区间。
合并之后,除去那些无用的区间(事实上在合并的时候可以将那些被合并的区间中后一个区间长度变为0),获得一个新的区间列表,在这个新的区间列表找寻最大长度区间和最大中断区间间隔即可获得解。
具体d的c++代码如下:
#include<fstream>
#include<string>
using namespace std;
bool IsInRange(int a,int b,int n);
int main()
{
ifstream fin ("milk2.in");
ofstream fout ("milk2.out");
int n;
int max_c=0; // max continuous time
int max_b=0; // max break time
int a[5000][2];
int fa[5000][2];//过滤a后的数组
int len=0;
int c=0;
int b=0;
fin>>n;
for(int i=0;i<n;i++)//获得输入
{
fin>>a[i][0]>>a[i][1];
}
//先排序,以farm开始的时间即区间的左端点从小到大排序
for(int i=1;i<n;i++)
{
int a1,a2;
if(a[i][0]<a[i-1][0])
{
int k=i;
while(a[k][0]<a[k-1][0])//swap,交换
{
a1=a[k][0];
a2=a[k][1];
a[k][0]=a[k-1][0];
a[k][1]=a[k-1][1];
a[k-1][0]=a1;
a[k-1][1]=a2;
k--;
if(k==0)
break;
}
}
}
//合并1.交 2.包含 3.无交集
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
int s0=a[i][0]; //初始化两个区间
int e0=a[i][1];
int s1=a[j][0];
int e1=a[j][1];
if(IsInRange(s0,e0,s1))//判断左端点
{
if(IsInRange(s0,e0,e1))//判断右端点,包含关系,合并区间
{
a[j][0]=a[i][1]; //[3,9],[4,7]->[3,9][3,3]
a[j][1]=a[i][0];
a[j][0]=a[i][0];
}
else//交集
{
a[i][1]=a[j][1]; //[3,5],[4,7]->[3,7],[3,3]
a[j][0]=a[i][0];
a[j][1]=a[i][0];
}
}
else //无交集
{
break;
}
}
}
len=0;
for(int i=0;i<n;i++)//移除[3,3],[12,12]这样的无效项
{
int t1=a[i][0];
int t2=a[i][1];
if(t1!=t2)
{
fa[len][0]=t1;
fa[len][1]=t2;
len++;
}
}
max_c=a[0][1]-a[0][0];//第一个区间的长度
max_b=0; //初始间隔为0
int k=0;
for(int i=0;i<n-1;i++)
{
int c=fa[i+1][1]-fa[i+1][0];//区间长度
int b=fa[i+1][0]-fa[i][1];//区间间隔
if(c>max_c) //保留区间长度的最大值
{
max_c=c;
}
if(b>max_b) //保留区间间隔的最大值
{
max_b=b;
b=0;
}
}
fout<<max_c<<" "<<max_b<<endl;
return 0;
}
bool IsInRange(int a,int b,int n) //判断n是否属于[a,b]
{
if(n>=a&&n<=b)
{
return true;
}
else
{
return false;
}
}
以上算法可以优化,不用合并区间,直接寻找解也可以。
代码如下:
#include<fstream>
#include<string>
using namespace std;
bool IsInRange(int a,int b,int n);
void QuikSort(int a[][2],int left,int right);
int main()
{
ifstream fin ("milk2.in");
ofstream fout ("milk2.out");
//ofstream ftest("m.out");
int n;
int max_c=0; // max continuous time
int max_b=0; // max break time
int a[5000][2];
int len=0;
int c=0;
int b=0;
fin>>n;
fin>>a[0][0]>>a[0][1];
for(int i=1;i<n;i++)//获得输入
{
fin>>a[i][0]>>a[i][1];
}
QuikSort(a,0,n-1);
int cur[2];
cur[0]=a[0][0];
cur[1]=a[0][1];
max_c=a[0][1]-a[0][0];//第一个区间的长度
max_b=0; //初始间隔为0
for(int i=1;i<n;i++) //寻找最大最小值
{
if(a[i][0]>cur[1])
{
b=a[i][0]-cur[1];
if(b>max_b) max_b=b;
c=cur[1]-cur[0];
if(c>max_c) max_c=c;
cur[0]=a[i][0];
cur[1]=a[i][1];
}
else
{//3,67, 4,87, 5,77 3->87 3->87
if(a[i][1]>cur[1]) cur[1]=a[i][1];
}
}
fout<<max_c<<" "<<max_b<<endl;
return 0;
}
void QuikSort(int a[][2],int left,int right)//快速排序
{
if(left<right)
{
int s=a[left][0]; //哨兵,即参考值X
int s1=a[left][1];
int i=left+1;
int j=right;
while(true)
{
while(a[i][0]<s&&i<right)
i++;
while(a[j][0]>s&&j>=0)
j--;
if(i>=j) break;
int a1,a2;
a1=a[i][0];
a2=a[i][1];
a[i][0]=a[j][0];
a[i][1]=a[j][1];
a[j][0]=a1;
a[j][1]=a2;
i++;
j--;
}
a[left][0]=a[j][0];
a[left][1]=a[j][1];
a[j][0]=s;
a[j][1]=s1;
QuikSort(a,left,j-1);
QuikSort(a,j+1,right);
}
}
bool IsInRange(int a,int b,int n) //判断n是否属于[a,b]
{
if(n>=a&&n<=b)
{
return true;
}
else
{
return false;
}
}