1.2.1 USACO Milking Cows

Milking Cows

Three farmers rise at 5 am each morning and head for the barn to milk three cows. The first farmer begins milking his cow at time 300 (measured in seconds after 5 am) and ends at time 1000. The second farmer begins at time 700 and ends at time 1200. The third farmer begins at time 1500 and ends at time 2100. The longest continuous time during which at least one farmer was milking a cow was 900 seconds (from 300 to 1200). The longest time no milking was done, between the beginning and the ending of all milking, was 300 seconds (1500 minus 1200).

Your job is to write a program that will examine a list of beginning and ending times for N (1 <= N <= 5000) farmers milking N cows and compute (in seconds):

  • The longest time interval at least one cow was milked.
  • The longest time interval (after milking starts) during which no cows were being milked.

PROGRAM NAME: milk2

INPUT FORMAT

Line 1: The single integer, N
Lines 2..N+1: Two non-negative integers less than 1,000,000, respectively the starting and ending time in seconds after 0500

SAMPLE INPUT (file milk2.in)

3
300 1000
700 1200
1500 2100

OUTPUT FORMAT

A single line with two integers that represent the longest continuous time of milking and the longest idle time.

SAMPLE OUTPUT (file milk2.out)

900 300

翻譯:https://www.luogu.com.cn/problem/P1204

題目描述

三個農民每天清晨 55 點起牀,然後去牛棚給三頭牛擠奶。

第一個農民在 300300 秒 (從 55 點開始計時) 給他的牛擠奶,一直到 10001000 秒。第二個農民在 700700 秒開始,在 12001200 秒結束。第三個農民在 15001500 秒開始,21002100 秒結束。

期間最長的至少有一個農民在擠奶的連續時間爲 900900 秒 (從 300300 秒到 12001200 秒),而最長的無人擠奶的連續時間(從擠奶開始一直到擠奶結束)爲 300300 秒 (從 12001200 秒到 15001500 秒)。


你的任務是編一個程序,讀入一個有 nn 個農民擠 nn 頭牛的工作時間列表,計算以下兩點(均以秒爲單位):

最長至少有一人在擠奶的時間段。

最長的無人擠奶的時間段。(從有人擠奶開始算起)

輸入格式

第一行一個正整數 nn

接下來 nn 行,每行兩個非負整數 l,rl,r,表示一個農民的開始時刻與結束時刻。

輸出格式

一行,兩個整數,即題目所要求的兩個答案。

輸入輸出樣例

輸入 #1複製

3
300 1000
700 1200
1500 2100

輸出 #1複製

900 300

說明/提示

【數據範圍】
對於 100\%100% 的數據,1\le n \le 50001≤n≤5000,1 \le l \le r \le 10^61≤l≤r≤106。

題目翻譯來自NOCOW。

USACO Training Section 1.2

思路1:使用標記數組的思想,將擠奶的時間段設置爲1,不擠奶的時間段設置爲0.注意此題是從擠奶的時間開始算的。比如:

樣例1:

1

5 6

這個樣例的結果應該爲1 0,而不是1,4

在進行標記的時候不能按照時間點標記。比如:

樣例2:

2

1 2 

3 4

這個樣例的結果應該爲1,1,而不是4,0.

因此標記的時候可以定義一個標記數組a.a[i]=1表示i->i+1時間段在擠奶。a[i]=0,表示i->i+1時間段沒有擠奶。

如樣例2,此時a[2]=1,a[3]=0,a[4]=1.循環查找時也是從for 2...4

/*
ID: L
PROG: milk2
LANG: C++ 
*/ 
#include<iostream>
#define N 1000005
using namespace std;
int a[N];
int main()
{
	freopen("milk2.in","r",stdin);
	freopen("milk2.out","w",stdout);
	int n;
	cin >> n;
	int l,r,minl = N, maxr = 0;
	while(n--)
	{
		cin >> l >> r;
		for(int i = l; i < r; ++i)//將工作的區間段設爲1,比如a[2]=1表示1-2s在工作 
		{
			a[i+1] = 1;
		 } 
		minl = min(minl,l);
		maxr = max(maxr,r);
	 } 
	int sum1 = 0, sum2 = 0, maxsum1 = 0, maxsum2 = 0;//sum1表示連續工作的時間和,maxsum1表示多個連續工作時間和裏面的最大值 
	for(int i = minl+1; i <= maxr; ++i)//i要從minl+1開始循環,因爲a[i]表示的時i->i+1時間在工作 
	{
		if(a[i] == 1) sum1++;
		if(a[i] == 0) sum2++;
		if(a[i] != a[i+1])
		{
			maxsum1 = max(maxsum1,sum1);
			maxsum2 = max(maxsum2,sum2);
			sum1 = sum2 = 0;//重置 
		}
	}
	cout << maxsum1 << " " << maxsum2 << endl; 
	return 0;
} 

使用差分和前綴和優化標記a數組的過程。

如樣例1

2

1 2

2 5

cf[i]  cf[2]=1,cf[3]=-1,cf[3]=0,cf[6]=1

/*
ID: L
PROG: milk2
LANG: C++ 
*/ 
#include<iostream>
#define N 1000005
using namespace std;
int a[N];
int cf[N];
int main()
{
	//freopen("milk2.in","r",stdin);
	//freopen("milk2.out","w",stdout);
	int n;
	cin >> n;
	int l,r,minl = N, maxr = 0;
	while(n--)//使用差分數組優化標記擠奶時間段的過程 
	{
		cin >> l >> r;
		cf[l+1] += 1;
		cf[r+1] -= 1;
		minl = min(minl,l);
		maxr = max(maxr,r);
	}
	int sum1 = 0, sum2 = 0, maxsum1 = 0, maxsum2 = 0;
	for(int i = minl; i <= maxr; ++i)
	{
		cf[i] = cf[i-1] + cf[i];//恢復差分數組 ,cf[3]=2表示3-4這個時間段在工作,且有工作時間段重合了兩次 
	//	cout << cf[i] << " ";
	}
//	cout << endl;
	for(int i = minl+1; i <= maxr; ++i)
	{
	//	cout << sum1 << " " << sum2 << endl;
		if(cf[i] >= 1) sum1++;
		if(cf[i] == 0) sum2++;
		maxsum1 = max(sum1,maxsum1);
		maxsum2 = max(sum2,maxsum2);
		if(cf[i] != cf[i+1] && (cf[i] == 0 || cf[i+1] == 0)) sum1 = sum2 = 0; 
	} 
	cout << maxsum1 << " " << maxsum2 << endl;
	return 0;
} 

解法3:使用區間相減的方法,這裏直接粘貼下大佬的題解:https://www.luogu.com.cn/problemnew/solution/P1204

介紹一種本題的貪心解法。

本題要求讀入一些擠牛奶的時間段,求最長至少有一人在擠牛奶的時間段和最長沒有人在擠牛奶的時間段。把讀入的區間視作線段,則題意轉變爲求至少有一條線段覆蓋的最大區間和沒有線段覆蓋的區間

假設讀入數據如下: fig

首先按照4條線段的起點位置排序(具體原因後面解釋)。將begin設置爲第一條線段的起點,將end設置爲第一條線段的終點。

然後從第二條線段開始判斷。如果該線段的起點小於end,則說明這兩條線段有重合部分,將end更新爲max{end,該線段的終點位置}。如果該線段的起點大於end,則說明該線段及以後的線段再也不會與前面的線段產生任何重合部分(這也就是排序的作用),那麼可以更新ans1和ans2的值:ans1更新爲max{ans1,end-begin},ans2更新爲max{ans2,該線段的起點位置-end}。具體參見圖中第4條線段,ans1被更新爲1200-0,ans2被更新爲1400-1200。

程序已經基本成型,但要注意在輸出答案前更新一遍ans1的值,這是爲了避免所有線段均有重合部分而無法判斷的情況。另外,ans1和ans2要初始化爲0。

#include<iostream>
#include<algorithm> 
using namespace std;
struct t{
	int begin;
	int end;
};
t a[5005];
bool cmp(t x, t y)//按照開始時間排序 
{
	if(x.begin <= y.begin) return true;
	else return false;
}
int main()
{
	int n;
	cin >> n;
	for(int i = 1; i <= n; ++i) cin >> a[i].begin >> a[i].end;
	sort(a+1,a+1+n,cmp);
	//for(int i = 1; i <= n; ++i) cout << a[i].start << " " << a[i].end << endl;
	int begin = a[1].begin;
	int end = a[1].end;
	int ans1 = 0, ans2 = 0;
	for(int i = 2; i <= n; ++i) 
	{
		if(a[i].begin <= end)//時間有重合,比較第二次開始時間和第一次結束時間 
			end = max(end,a[i].end);
		else//時間沒有重合 
		{
			ans1 = max(ans1,end-begin);
			ans2 = max(ans2,a[i].begin-end);
			begin = a[i].begin;
			end = a[i].end;
		}
	}
	ans1 = max(ans1,end-begin);//只有一個樣例的情況,比如1  5 6,答案爲1
	cout << ans1 << " " << ans2 << endl;
	return 0;
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章