方法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; }
方法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; }