Week3--作業--C--區間覆蓋 [貪心算法]

題目描述

數軸上有 n (1<=n<=25000)個閉區間 [ai, bi],選擇儘量少的區間覆蓋一條指定線段 [1, t] (1<=t<=1,000,000)。覆蓋整點,即(1,2)+(3,4)可以覆蓋(1,4)。不可能辦到輸出-1。

輸入

第一行:N和T
第二行至N+1行:
每一行一個閉區間。

輸出

  輸出選擇的區間的數目,
不可能辦到輸出-1

樣例輸入

3 10
1 7
3 6
6 10

樣例輸出

2

思路

綜述

這個題和這周做的B題十分類似,也是將一個區間組合排序,然後依次選擇;用到的算法也是貪心算法;
排序的準則是:按照按照右端點從頭到尾的升序排列;

貪心的準則:

假定當前要覆蓋的區間是[begin,end];閉區間)
下一次選擇的區間滿足以下條件:
1)區間的末端點大於於等於end;
2)區間的左端點是滿足條件1中的最小的一個
如果某次選擇沒有可用區間,並且要覆蓋的區間還未空,則退出返回-1,否則返回選擇的區間的個數;

過程

Step1:輸入

再輸入的時候,進行一遍篩選,將右端點小於begin或者左端點大於end的清除

	for (i = 0; i < n; i++) {
		 P xx;
		cin >> xx.a >> xx.b;
		if (xx.a<1 || xx.b>t) continue;
		qq.push(xx);
	}
Step2:選擇

第一步判斷
右端點最大的是否大於等於end

	if (qq.top().b < t) {
		std::cout << "-1" << endl;
		return 0;
	}

循環選擇:

		while (!qq.empty()) {
		if (qq.top().b >= t) {//找到右端點在end之後的
			if (qq.top().a < flag)
				flag = qq.top().a;//找出符合的點中左端點最小的一個
			if (flag <= 1) {
				total++;//已經覆蓋整個區間,退出並且輸出結果
				std::cout << total << endl;
				return 0;
			}
			qq.pop();
		}
		else {
		//找到之後進行更新
			t = flag - 1;
			flag = t;
			total++;//區間選擇總數加一
			if (qq.top().b < t) {
				std::cout << "-1" << endl;
				return 0;
			}
		}
	}

總結

1、使用快速讀入scanf可以節省時間
2、貪心的準則有很多,需要找出並且證明一個正確的
記錄:(剛開始錯誤的想法)
錯誤1:在多關鍵字排序的時候,排序的準則如下
1、右端點需要大於等於t
2、左端點小的排在前面
但是這樣是錯誤的,因爲更新過一次之後,再選擇區間的時候,就會選擇出並不符合題意的區間
錯誤2:在錯誤1的基礎上優化,但是優化方式不對,導致超時
錯誤優化:每次選擇完區間都進行重新排序,右端點大於等於當前要覆蓋的區間的end,這樣複雜度在O(n^2*log)級別上,嚴重超時。

代碼

#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;
int t;
int total = 0;
struct P {
	int a, b;
	bool operator <(const struct P& p)const {
		return b < p.b;
	}
};
int main() {
	int n, i;
	cin >> n >> t;
	priority_queue<P> qq ;
	//讀入數據
	for (i = 0; i < n; i++) {
		 P xx;
		cin >> xx.a >> xx.b;
		//初步篩選
		if (xx.a<1 || xx.b>t) continue;
		qq.push(xx);
	}
	int step = 1;
	//初步判斷
	if (qq.top().b < t) {
		std::cout << "-1" << endl;
		return 0;
	}
	int flag = 1000000;
	while (!qq.empty()) {
	//選點
		if (qq.top().b >= t) {
		//找出最優的點
			if (qq.top().a < flag)
				flag = qq.top().a;
			if (flag <= 1) {
				total++;
				std::cout << total << endl;
				return 0;
			}
			qq.pop();
		}
		else {
			t = flag - 1;
			flag = t;
			total++;//更新
			if (qq.top().b < t) {
				std::cout << "-1" << endl;
				return 0;
			}
		}
	}

	//不行
	std::cout << "-1" << endl;


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