POJ 3190 Stall Reservations-奶牛分栏(区间贪心,优先队列)
2014-11-11 20:03
316 查看
http://poj.org/problem?id=3190
题目大意:每一只奶牛要求在时间区间[A,B]内独享一个牛栏。问最少需要多少个牛栏。
贪心策略是优先满足A最小的奶牛,维持一个牛栏B最小堆,将新来的奶牛塞进B最小的牛栏里。
unsigned int end; bool operator < (const Section& b) const { return begin < b.begin; } }; struct Stall{ unsigned int id; unsigned int end; bool operator < (const Stall& b) const { return end > b.end; } Stall(){} Stall(unsigned int id, unsigned int end):id(id),
end(end){}}; #define MAX_COWS 50000Section cow[MAX_COWS];unsigned int result[MAX_COWS]; // 每头牛从属于哪个牛栏priority_queue<Stall> que; // 最小堆,储存所有牛栏区间的结束点(也就是最右端) inline void put_cow(const int& i, const bool& new_stall){ Stall s; if (new_stall) { s.id = que.size()
+ 1; } else { s.id = que.top().id; que.pop(); } s.end = cow[i].end; result[cow[i].index] = s.id; que.push(s);} ///////////////////////////SubMain//////////////////////////////////int main(int argc, char *argv[]){#ifndef ONLINE_JUDGE freopen("in.txt", "r",
stdin); freopen("out.txt", "w", stdout);#endif int N; cin >> N; for (int i = 0; i < N; ++i) { cow[i].index = i; cin >> cow[i].begin; cin >> cow[i].end; } sort(cow, cow + N); put_cow(0, true); for (int i = 1; i < N; ++i) { put_cow (i, cow[i].begin <= que.top().end);
} cout << que.size() << endl; for (int i = 0; i < N; ++i) { cout << result[i] << endl; }#ifndef ONLINE_JUDGE fclose(stdin); fclose(stdout); system("out.txt");#endif return 0;}///////////////////////////End Sub//////////////////////////////////
代码二
对cow进行排列。按L以及R从小到大排序。确保遍历的时候优先取到最小的L.
然后就可以对cow遍历求解。
华丽的分界线。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
对于这个题目,已知有两种贪心策略是正确的。
将
头牛按照开始时间
排序,每次选取牛集合
中的开始时间最早,并且与结束时间
不冲突的元素,找到之后从集合
中删除或者标记元素。当找不到满足开始时间 大于 结束时间这一条件的元素时,重新从
中选取元素,直到
为空。
将
头牛按照开始时间
排序,首先将第一头牛存入集合
中,如果接下来的牛满足开始时间大于
中最早结束时间,那么取出这个元素,并更新其时间结束时间为当前牛的结束时间,然后重新存入集合中。否则直接将该当前牛存入集合中。
贪心算法的证明主要是两种思路。
第一种 方法被称为 staying ahead [2],即保持领先,意义为如果我们运用某个策略,在算法的每一步中都使某个条件保持领先,那么最后可以利用这个领先条件来证明最优解。这种方法本质上结合了上面介绍的Induction和Contradiction方法。
接下来我们将这个方法应用到POJ 3190算法2的证明中。
首先,我们需要确定一个领先条件,我们设这个领先条件为:在算法的每一步中,都选取最小开始时间的畜栏放入下一头牛。即
,
代表当前策略,
代表最优策略,对于贪心算法的证明,基本假设是 当前贪心策略至少和最优策略保证相同的效果 。
证明这个条件在算法的每一步中都成立。运用Induction method,首先证明base case的情况,我们首先从开始时间0开始存入牛,所以显然成立。接下来,证明前k头牛时成立,k+1时也成立。两种情况,第k+1头牛的开始时间
小于 集合
中的最小结束时间,那么直接存入集合,这个时候要么最小时间更新为第
头牛的结束时间,要么保持原来的结束时间。否则,如果第k+1头牛的开始时间
大于 集合
中的最小结束时间,更新集合
最小结束时间元素信息。因此接下来仍然可以直接选取最小结束时间的畜栏。
接下来我们运用Contradiction method来证明我们的策略可以得到畜栏数最少的解决方案。由于我们已经证明每一步必定可选最小开始时间的畜栏,所以如果上述策略不成立,最优解
中一定存在某一畜栏,其中所有牛都可以被放入其他畜栏。那么,这些牛同样可以被放入到贪心策略解的畜栏中(因为我们证明了每一步选取的最小开始时间至少和最优解一致)。然而,在放入某一头牛的时候,如果其开始时间 大于 最小结束时间,那么一定已经被放入到之前的畜栏中去了,所以判定这些牛一定不能放入到原有的畜栏中去。
至此,证明结束。算法2可以得到最优解。
第二种 方法被称为 exchange arguments [2],即将最优解元素和当前贪心策略解元素逐个交换,交换的情况很多种[3],可以是
中存在的元素,
中不存在,也可以是顺序不同,如果不影响最优解效果,那么贪心策略成立。在我们的题目中,交换条件可以是 放的畜栏不同 。我们已知算法2是最优解,来证明算法1也是最优解。
交换
与
中的元素,
,
中如果存在放入的畜栏不同,只有一种情况:
c1 -------------|t1
c2 -------|t2
如果当前牛的开始时间
,算法1将不会处理这个元素,而是在第二轮中将这个元素放入到
中,而算法2直接将当前元素放入
,这种情况下两种算法结果相同。
如果当前牛的开始时间
,算法1将元素放入
,然而算法2会将元素放入
。但是这样的处理不会影响最优解,算法将元素放入到
后,
也就一定只能存入开始时间在
之后的元素,其他开始时间在
之前的元素将会被放入
。
c1 -------------|t1
c2 -------|t2 ------------|t3
而对于算法1而言:
c1 -------------|t1-----------|t3
c2 -------|t2
其他开始时间在
之前的元素将会被放入
,并且这些元素的开始时间一定
,所以放入
和放入
对算法最终的效果没有影响。因此,贪心策略成立
下面简单分析一下这个题目的三种解法:
算法1解法1 将元素放入set中,每次更新时,查找set,并且从中删除元素。放入元素复杂度,
。遍历复杂度,我们假设最坏情况,每次遍历删除一个元素:
算法1解法2 元素存入数组中,每次遍历数组,标志已经删除的元素。排序:
。遍历,同样是最坏情况,每次前进一个元素(二分查找):
算法2解法1 维护一个最小堆,每次更新最小堆。排序:
。最小堆维护,最坏情况,每次都无法更新最小堆:
题目大意:每一只奶牛要求在时间区间[A,B]内独享一个牛栏。问最少需要多少个牛栏。
贪心策略是优先满足A最小的奶牛,维持一个牛栏B最小堆,将新来的奶牛塞进B最小的牛栏里。
<p><span style="color: rgb(51, 51, 51); font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px; text-indent: 28px; background-color: rgb(240, 240, 240);"><span style="font-size:12px;">#include <iostream></span></span></p><p><span style="font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; color: rgb(51, 51, 51); line-height: 25px; text-indent: 28px; background-color: rgb(240, 240, 240);"><span style="font-size:12px;">#include <algorithm></span></span></p><pre name="code" class="cpp" style="font-size: 14px; color: rgb(51, 51, 51); line-height: 25px;">#include <queue>using namespace std; struct Section{ unsigned int index; unsigned int begin;
unsigned int end; bool operator < (const Section& b) const { return begin < b.begin; } }; struct Stall{ unsigned int id; unsigned int end; bool operator < (const Stall& b) const { return end > b.end; } Stall(){} Stall(unsigned int id, unsigned int end):id(id),
end(end){}}; #define MAX_COWS 50000Section cow[MAX_COWS];unsigned int result[MAX_COWS]; // 每头牛从属于哪个牛栏priority_queue<Stall> que; // 最小堆,储存所有牛栏区间的结束点(也就是最右端) inline void put_cow(const int& i, const bool& new_stall){ Stall s; if (new_stall) { s.id = que.size()
+ 1; } else { s.id = que.top().id; que.pop(); } s.end = cow[i].end; result[cow[i].index] = s.id; que.push(s);} ///////////////////////////SubMain//////////////////////////////////int main(int argc, char *argv[]){#ifndef ONLINE_JUDGE freopen("in.txt", "r",
stdin); freopen("out.txt", "w", stdout);#endif int N; cin >> N; for (int i = 0; i < N; ++i) { cow[i].index = i; cin >> cow[i].begin; cin >> cow[i].end; } sort(cow, cow + N); put_cow(0, true); for (int i = 1; i < N; ++i) { put_cow (i, cow[i].begin <= que.top().end);
} cout << que.size() << endl; for (int i = 0; i < N; ++i) { cout << result[i] << endl; }#ifndef ONLINE_JUDGE fclose(stdin); fclose(stdout); system("out.txt");#endif return 0;}///////////////////////////End Sub//////////////////////////////////
代码二
对cow进行排列。按L以及R从小到大排序。确保遍历的时候优先取到最小的L.
然后就可以对cow遍历求解。
#include <iostream> #include <queue> #include <algorithm> #include<functional> using namespace std; struct N { int l,r,id,stall; bool operator <(const N &a)const { return a.r<r; } }cow[50010]; bool cmp(N &a,N &b) { return a.l<b.l; } int main() { priority_queue<N>que; int n; cin>>n; for(int i=1;i<=n;i++) { cin>>cow[i].l>>cow[i].r; cow[i].id=i; } sort(cow+1,cow+n+1,cmp); cow[0].stall=1; cow[0].r=0; que.push(cow[0]); int a[50010]; int S=2; for(int i=1;i<=n;i++) { N c=que.top(); if(cow[i].l>c.r) { que.pop(); cow[i].stall=c.stall; a[cow[i].id]=c.stall; que.push(cow[i]); } else { cow[i].stall=S; a[cow[i].id]=S++; que.push(cow[i]); } } cout<<S-1<<endl; for(int i=1;i<=n;i++) cout<<a[i]<<endl; return 0; }
华丽的分界线。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
对于这个题目,已知有两种贪心策略是正确的。
将
头牛按照开始时间
排序,每次选取牛集合
中的开始时间最早,并且与结束时间
不冲突的元素,找到之后从集合
中删除或者标记元素。当找不到满足开始时间 大于 结束时间这一条件的元素时,重新从
中选取元素,直到
为空。
将
头牛按照开始时间
排序,首先将第一头牛存入集合
中,如果接下来的牛满足开始时间大于
中最早结束时间,那么取出这个元素,并更新其时间结束时间为当前牛的结束时间,然后重新存入集合中。否则直接将该当前牛存入集合中。
2.2 贪心算法证明
贪心算法的证明主要是两种思路。第一种 方法被称为 staying ahead [2],即保持领先,意义为如果我们运用某个策略,在算法的每一步中都使某个条件保持领先,那么最后可以利用这个领先条件来证明最优解。这种方法本质上结合了上面介绍的Induction和Contradiction方法。
接下来我们将这个方法应用到POJ 3190算法2的证明中。
首先,我们需要确定一个领先条件,我们设这个领先条件为:在算法的每一步中,都选取最小开始时间的畜栏放入下一头牛。即
,
代表当前策略,
代表最优策略,对于贪心算法的证明,基本假设是 当前贪心策略至少和最优策略保证相同的效果 。
证明这个条件在算法的每一步中都成立。运用Induction method,首先证明base case的情况,我们首先从开始时间0开始存入牛,所以显然成立。接下来,证明前k头牛时成立,k+1时也成立。两种情况,第k+1头牛的开始时间
小于 集合
中的最小结束时间,那么直接存入集合,这个时候要么最小时间更新为第
头牛的结束时间,要么保持原来的结束时间。否则,如果第k+1头牛的开始时间
大于 集合
中的最小结束时间,更新集合
最小结束时间元素信息。因此接下来仍然可以直接选取最小结束时间的畜栏。
接下来我们运用Contradiction method来证明我们的策略可以得到畜栏数最少的解决方案。由于我们已经证明每一步必定可选最小开始时间的畜栏,所以如果上述策略不成立,最优解
中一定存在某一畜栏,其中所有牛都可以被放入其他畜栏。那么,这些牛同样可以被放入到贪心策略解的畜栏中(因为我们证明了每一步选取的最小开始时间至少和最优解一致)。然而,在放入某一头牛的时候,如果其开始时间 大于 最小结束时间,那么一定已经被放入到之前的畜栏中去了,所以判定这些牛一定不能放入到原有的畜栏中去。
至此,证明结束。算法2可以得到最优解。
第二种 方法被称为 exchange arguments [2],即将最优解元素和当前贪心策略解元素逐个交换,交换的情况很多种[3],可以是
中存在的元素,
中不存在,也可以是顺序不同,如果不影响最优解效果,那么贪心策略成立。在我们的题目中,交换条件可以是 放的畜栏不同 。我们已知算法2是最优解,来证明算法1也是最优解。
交换
与
中的元素,
,
中如果存在放入的畜栏不同,只有一种情况:
c1 -------------|t1
c2 -------|t2
如果当前牛的开始时间
,算法1将不会处理这个元素,而是在第二轮中将这个元素放入到
中,而算法2直接将当前元素放入
,这种情况下两种算法结果相同。
如果当前牛的开始时间
,算法1将元素放入
,然而算法2会将元素放入
。但是这样的处理不会影响最优解,算法将元素放入到
后,
也就一定只能存入开始时间在
之后的元素,其他开始时间在
之前的元素将会被放入
。
c1 -------------|t1
c2 -------|t2 ------------|t3
而对于算法1而言:
c1 -------------|t1-----------|t3
c2 -------|t2
其他开始时间在
之前的元素将会被放入
,并且这些元素的开始时间一定
,所以放入
和放入
对算法最终的效果没有影响。因此,贪心策略成立
2.3 时间复杂度分析
下面简单分析一下这个题目的三种解法:算法1解法1 将元素放入set中,每次更新时,查找set,并且从中删除元素。放入元素复杂度,
。遍历复杂度,我们假设最坏情况,每次遍历删除一个元素:
算法1解法2 元素存入数组中,每次遍历数组,标志已经删除的元素。排序:
。遍历,同样是最坏情况,每次前进一个元素(二分查找):
算法2解法1 维护一个最小堆,每次更新最小堆。排序:
。最小堆维护,最坏情况,每次都无法更新最小堆:
相关文章推荐
- poj 3190 Stall Reservations(区间贪心,优先队列)
- poj 3190 贪心(区间)+优先队列维护
- poj3190 区间贪心 + STL
- Stall Reservations(POJ 3190 贪心+优先队列)
- [POJ 3190] Stall Reservations (区间贪心)
- POJ 3190 Stall Reservations (贪心+优先队列)
- poj 3190 贪心+优先队列优化
- poj 3190 优先队列+贪心
- POJ 3190 Stall Reservations 【贪心 区间】
- POJ-3190-贪心(优先队列)
- poj 3190 Stall Reservations 贪心 + 优先队列
- 区间贪心·POJ-3190·Stall Reservations
- POJ 3190 Stall Reservations 贪心+优先队列或set
- (不易)POJ-3190 区间贪心
- POJ-3190-Stall Reservations-优先队列+贪心
- poj 3190 Stall Reservations (贪心+优先队列)
- POJ 3190 贪心+优先队列
- poj 3190 Stall Reservations (贪心+优先队列)
- POJ 3190 Stall Reservations(贪心+优先队列)
- POJ - 3190 Stall Reservations 贪心 + 优先队列