POJ3614 Sunscreen 貪心入門
阿新 • • 發佈:2018-05-23
range 因此 name poi 可能性 排序 can break inline 位於左區間,那麽\(a\notin [l_2,r_2]\Rightarrow a\in(-\infty ,l_2)\cup(r_2,b)\)。而如果我們讓\(b\)位於左區間,那麽\(b\notin [l_2,r_2]\Rightarrow b\in(-\infty,l_2)\cup(r_2,\infty)\)後者有兩個無窮,前者有一個無窮,故性質成立。其它的結論通過類似的方式推導,無法消掉一個無窮,故此方法是對的。
因此,我們可以得到推論:
處理區間集合\(R\)和點集\(A\)時,先將左端點最靠右的區間\(r\)與屬於該區間且最靠右的點\(a\)配對,然後子問題\(R-\{r\},A-\{a\}\) 所能配對的數量是最多的。
憑什麽就要選左端點最靠右的區間?因為先選了它並不會使結果變差。
由此我們就得到了貪心算法:給點按位置從大到小排序,區間按左端點位置從大到小排序,然後按以上黑體字做即可。
題目大意
給出一些區間和一些點,一個點如果在一個區間內,那麽此兩者可以匹配。問匹配數最大是多少。
題解
這樣的題我們一般都是站在區間上去找與其配對的點。我們可以得到如下性質:
對於一段區間\([l_1,r_1]\)的任意兩點\(a,b, a<b\),它們對於任意一個區間\([l_2,r_2],l_2<l_1\),\(a\in[l_2,r_2]\)的可能性(以後用P表示)\(P(a\in[l_2,r_2])>P(b\in[l_2,r_2])\)。
什麽叫“可能性大”呢?暫且規定如果事件A不可能發生的自變量的取值範圍比事件B的少一個“無限”,則事件A成立的可能性比B的大。本題中,如果我們讓\(a\)
因此,我們可以得到推論:
處理區間集合\(R\)和點集\(A\)時,先將左端點最靠右的區間\(r\)與屬於該區間且最靠右的點\(a\)配對,然後子問題\(R-\{r\},A-\{a\}\)
憑什麽就要選左端點最靠右的區間?因為先選了它並不會使結果變差。
由此我們就得到了貪心算法:給點按位置從大到小排序,區間按左端點位置從大到小排序,然後按以上黑體字做即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_POINT = 3000, MAX_RANGE = 3000;
struct Point
{
int Num, P;
}_points[MAX_POINT];
struct Range
{
int L, R;
}_ranges[MAX_RANGE];
bool CmpPoint(Point& a, Point& b)
{
return a.P > b.P;
}
bool CmpRange(Range& a, Range& b)
{
return a.L > b.L;
}
int main()
{
int totRange, totPoint;
scanf("%d%d", &totRange, &totPoint);
for (int i = 1; i <= totRange; i++)
scanf("%d%d", &_ranges[i].L, &_ranges[i].R);
for (int i = 1; i <= totPoint; i++)
scanf("%d%d", &_points[i].P, &_points[i].Num);
sort(_ranges + 1, _ranges + totRange + 1, CmpRange);
sort(_points + 1, _points + totPoint + 1, CmpPoint);
int ans = 0;
for (int i = 1; i <= totRange; i++)
{
for (int j = 1; j <= totPoint; j++)
{
if (_points[j].Num > 0 && _ranges[i].L <= _points[j].P && _points[j].P <= _ranges[i].R)
{
ans++;
_points[j].Num--;
break;
}
}
}
printf("%d\n", ans);
return 0;
}
POJ3614 Sunscreen 貪心入門