USACO 1.2 Milking Cows

方法1: 这方法名字我也不知道算啥

a[i].l, a[i].r表示一个农民的工作左右区间。

按照l关键字排序,记录当前区间[p.l,p.r]

然后从排序后的农民从头到尾扫描, 查看当前农民是否在这个区间内,如果第二个农民和第一个农民的区间【重合】【分开】就分别更新当前的区间,并且记录挤奶和没有挤奶的时间的最大值。

没有啥难度,时间复杂度O(nlogn) 就是排序的时间而已,常数很小代码也短

/*
ID:xueyifa4
PROG:milk2
LANG:C++
*/
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int BESAVE = 5;
const int max_n = 5000 + BESAVE; 
int n, milking, nomilking = 0;
class point
{
	public:
	int l, r;
}a[max_n], p;
bool cmp(point A, point B)
{
	return A.l < B.l;	
}
int main()
{
	freopen("milk2.in","r",stdin);
	freopen("milk2.out","w",stdout);
	scanf("%d", &n);
	for (int i = 0; i != n; ++ i)	scanf("%d%d", &a[i].l, &a[i].r);
	sort(a, a + n, cmp);
	p = a[0];
	milking = p.r - p.l;
	for (int i = 1; i != n; ++ i)
	{
		if (a[i].l <= p.r)
		{
			if (a[i].r <= p.r)	continue;
			p.r = a[i].r;
			milking = max(milking, p.r - p.l);
			continue;
		}
		nomilking = max(nomilking, a[i].l - p.r);	
		p = a[i];
	}
	printf("%d %d\n", milking, nomilking);
	return 0;
}

方法2:并查集  (coding)


方法3:线段树 (coding)

好久没写这个了- - WA了3次,

还有又体会到长变量的好处了,这次写短变量名,好几次读写错l,r ,以后不偷懒,写全left ,right了。。虽然多几个字母,但是错误率会降低很多QAQ

/*
ID:xueyifa4
PROG:milk2
LANG:C++
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <map>
using namespace std;

int n;
const int max_n = 5000 + 10;
int link[max_n * 2];

struct farm
{
	int L, R;
}a[max_n];

map<int, int>q;

struct point
{
	int LL, RR;// (]区间
	point *pl, *pr;
	bool  cover; //cd 信息是否传递 cover是否已经全部被覆盖
	int cml, cmr, nml, nmr;//最左边的连续区间长度, 非连续区间长度
	int cs, ns; //最长区间
	point(int L, int R):LL(L),RR(R),pl(NULL), pr(NULL),cs(0), ns(link[R] - link[L]),nml(link[R] - link[L]), nmr(link[R] - link[L]), cover(false){}
	point(){}
}root;


inline void tran(point &k) //k从子节点接受信息上传
{
	if (k.cover)	return; //如果已经全部覆盖了,那么没有覆盖的必要了
	int max_cover = max(k.pl -> cs, k.pr -> cs); //最长覆盖区间,是两个子区间的最长区间中最大的
	int tmp = k.pl -> cmr + k.pr -> cml; //tmp是2个子区间的合并区间
	k.cs = max(max_cover, tmp); //最长覆盖区间自然是选择  子区间的最长覆盖区间,或者合并后的新的最长覆盖区间
	int max_nocover = max(k.pl -> ns, k.pr -> ns); //最长未覆盖区间,同理
	tmp = k.pl -> nmr + k.pr -> nml;
	k.ns = max(max_nocover, tmp);
	k.cml = k.pl -> cml;//左右最长的覆盖和没覆盖的地方直接更新
	k.nml = k.pl -> nml;
	k.cmr = k.pr -> cmr;
	k.nmr = k.pr -> nmr;

	if (k.pl -> cover)	k.cml = k.pl -> cs + k.pr -> cml; //如果左区间全部覆盖,那么最长左区间就是左区间全段 + 右区间左边
	if (k.pr -> cover)	k.cmr = k.pr -> cs + k.pl -> cmr;
	if (!k.pl -> cs)	k.nml = k.pl -> ns + k.pr -> nml;
	if (!k.pr -> cs)	k.nmr = k.pr -> ns + k.pl -> nmr;

	if (k.cs == link[k.RR] - link[k.LL])	k.cover = true; //如果最长区间是全部被覆盖了,那么直接返回true
}

void insert(point &k, int L, int R)
{
	int x, y, mid = (k.LL + k.RR) / 2;
	if (k.cover)	return;//已经全部被覆盖了,那么不用覆盖了,直接返回
	if (k.LL + 1 != k.RR && k.pl == NULL) // (5,5] 也就是点,首先不是点,而且没有子节点,那么建立子节点
	{
		k.pl = new point(k.LL, mid);
		k.pr = new point(mid, k.RR);
	}
	if (k.LL == L && k.RR == R)//找到匹配区间
	{
		k.cover = true; //已经被全部覆盖
		k.cs = link[R] - link[L];//区间全部覆盖长度肯定是  最右-最左
		k.ns = 0;   //最长空余 
		k.cml = k.cmr = k.cs;//左右最长被覆盖区间为 整个区间的长度
		k.nml = k.nmr = 0;//左右没有被覆盖的长度为0
		return;
	}
	if (R<= mid)// 如果最右边的区间和mid平齐,那么直接搞
	{
		insert(*k.pl, L, R);
		tran(k);
		return;
	}
	if (mid <= L)
	{
		insert(*k.pr, L, R);
		tran(k);
		return;
	}
	insert(*k.pl, L, mid);
	insert(*k.pr, mid, R);
	tran(k);
}

int main()
{
	freopen("milk2.in","r",stdin);
	freopen("milk2.out","w",stdout);
	scanf("%d", &n);
	for (int i = 0; i != n; ++ i)	
	{
		scanf("%d%d", &a[i].L, &a[i].R);
		q[a[i].L] = 0;
		q[a[i].R] = 0;
	}
	int t = 0;
	for (map<int, int>::iterator it = q.begin(); it != q.end(); ++ it)
	{
		it -> second = ++ t;	
		link[t] = it -> first;
	}
	root.LL = 1;
	map<int, int>::iterator it = q.end();
	--it;
	root.RR = it -> second;
	for (int i = 0; i != n; ++ i)	insert(root, q[a[i].L], q[a[i].R]);
	cout<< root.cs<<" "<<root.ns<<endl;
	return 0;
}


你可能感兴趣的:(USACO)