程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> POJ 3190 Stall Reservations-奶牛分欄(區間貪心,優先隊列)

POJ 3190 Stall Reservations-奶牛分欄(區間貪心,優先隊列)

編輯:C++入門知識

POJ 3190 Stall Reservations-奶牛分欄(區間貪心,優先隊列)


http://poj.org/problem?id=3190

題目大意:每一只奶牛要求在時間區間[A,B]內獨享一個牛欄。問最少需要多少個牛欄。

貪心策略是優先滿足A最小的奶牛,維持一個牛欄B最小堆,將新來的奶牛塞進B最小的牛欄裡。


#include

#include

#include 
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 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 
#include 
#include 
#include
using namespace std;
struct N
{
	int l,r,id,stall;
	bool operator <(const N &a)const
	{
		return a.rque;
	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<
華麗的分界線。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

對於這個題目,已知有兩種貪心策略是正確的。

  1. N 頭牛按照開始時間 t(i) 排序,每次選取牛集合 S 中的開始時間最早,並且與結束時間 f(i) 不沖突的元素,找到之後從集合 S 中刪除或者標記元素。當找不到滿足開始時間 大於 結束時間這一條件的元素時,重新從 S 中選取元素,直到 S 為空。
  2. N 頭牛按照開始時間 t(i) 排序,首先將第一頭牛存入集合 T 中,如果接下來的牛滿足開始時間大於 T 中最早結束時間,那麼取出這個元素,並更新其時間結束時間為當前牛的結束時間,然後重新存入集合中。否則直接將該當前牛存入集合中。

    2.2 貪心算法證明

    貪心算法的證明主要是兩種思路。

    第一種 方法被稱為 staying ahead [2],即保持領先,意義為如果我們運用某個策略,在算法的每一步中都使某個條件保持領先,那麼最後可以利用這個領先條件來證明最優解。這種方法本質上結合了上面介紹的Induction和Contradiction方法。

    接下來我們將這個方法應用到POJ 3190算法2的證明中。

    1. 首先,我們需要確定一個領先條件,我們設這個領先條件為:在算法的每一步中,都選取最小開始時間的畜欄放入下一頭牛。即 w(i) <= o(i)w 代表當前策略,o 代表最優策略,對於貪心算法的證明,基本假設是 當前貪心策略至少和最優策略保證相同的效果
    2. 證明這個條件在算法的每一步中都成立。運用Induction method,首先證明base case的情況,我們首先從開始時間0開始存入牛,所以顯然成立。接下來,證明前k頭牛時成立,k+1時也成立。兩種情況,第k+1頭牛的開始時間 t(k+1) 小於 集合T 中的最小結束時間,那麼直接存入集合,這個時候要麼最小時間更新為第 k 頭牛的結束時間,要麼保持原來的結束時間。否則,如果第k+1頭牛的開始時間 t(k+1)大於 集合 T 中的最小結束時間,更新集合 T 最小結束時間元素信息。因此接下來仍然可以直接選取最小結束時間的畜欄。
    3. 接下來我們運用Contradiction method來證明我們的策略可以得到畜欄數最少的解決方案。由於我們已經證明每一步必定可選最小開始時間的畜欄,所以如果上述策略不成立,最優解 o 中一定存在某一畜欄,其中所有牛都可以被放入其他畜欄。那麼,這些牛同樣可以被放入到貪心策略解的畜欄中(因為我們證明了每一步選取的最小開始時間至少和最優解一致)。然而,在放入某一頭牛的時候,如果其開始時間 大於 最小結束時間,那麼一定已經被放入到之前的畜欄中去了,所以判定這些牛一定不能放入到原有的畜欄中去。
    4. 至此,證明結束。算法2可以得到最優解。

      第二種 方法被稱為 exchange arguments [2],即將最優解元素和當前貪心策略解元素逐個交換,交換的情況很多種[3],可以是 o

      中存在的元素, w 中不存在,也可以是順序不同,如果不影響最優解效果,那麼貪心策略成立。在我們的題目中,交換條件可以是 放的畜欄不同 。我們已知算法2是最優解,來證明算法1也是最優解。

      交換 o

      w 中的元素, ow 中如果存在放入的畜欄不同,只有一種情況:

      c1 -------------|t1

      c2 -------|t2

      如果當前牛的開始時間 <t1

      ,算法1將不會處理這個元素,而是在第二輪中將這個元素放入到 c2 中,而算法2直接將當前元素放入 c2 ,這種情況下兩種算法結果相同。

      如果當前牛的開始時間 >t1

      ,算法1將元素放入 c1 ,然而算法2會將元素放入 c2 。但是這樣的處理不會影響最優解,算法將元素放入到 c2 後, c2 也就一定只能存入開始時間在 t3 之後的元素,其他開始時間在 t3 之前的元素將會被放入 c1

      c1 -------------|t1

      c2 -------|t2 ------------|t3

      而對於算法1而言:

      c1 -------------|t1-----------|t3

      c2 -------|t2

      其他開始時間在 t3

      之前的元素將會被放入 c2 ,並且這些元素的開始時間一定 >t1 ,所以放入 c1 和放入c1 對算法最終的效果沒有影響。因此,貪心策略成立

      2.3 時間復雜度分析

      下面簡單分析一下這個題目的三種解法:

      • 算法1解法1 將元素放入set中,每次更新時,查找set,並且從中刪除元素。放入元素復雜度,log(1) + log(2) ...+log(n) = log(n!)。遍歷復雜度,我們假設最壞情況,每次遍歷刪除一個元素: 2log(n!)
      • 算法1解法2 元素存入數組中,每次遍歷數組,標志已經刪除的元素。排序:nlog(n) 。遍歷,同樣是最壞情況,每次前進一個元素(二分查找):log(n) + log(n-1) + log(1) = log(n!)
      • 算法2解法1 維護一個最小堆,每次更新最小堆。排序: nlog(n) 。最小堆維護,最壞情況,每次都無法更新最小堆:log(n) + log(n-1) + log(1) = log(n!)



  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved