與很多奶牛一樣,FJ那羣養尊處優的奶牛們對食物越來越挑剔,隨便拿堆草就能打發她們午飯的日子自然是一去不返了。現在FJ不得不去牧草專供商那裏購買大量美味多汁的牧草,來滿足他那 N 頭挑剔的奶牛。
所有奶牛都對FJ提出了她對牧草的要求:第i頭奶牛要求她的食物每份的價錢不低於A_i,並且鮮嫩程度不能低於B_i。商店裏供應M種不同的牧草,第i種牧草的定價爲C_i,鮮嫩程度爲D_i 。
爲了顯示她們的與衆不同,每頭奶牛都要求她的食物是獨一無二的,也就是說,沒有哪兩頭奶牛會選擇同一種食物。FJ想知道,爲了讓所有奶牛滿意,他最少得在購買食物上花多少錢。
【輸入格式】
第1行: 2個用空格隔開的整數N 和 M。第2..N+1行: 第i+1行包含2個用空格隔開的整數:A_i、B_i。第N+2..N+M+1行: 第j+N+1行包含2個用空格隔開的整數:C_i、D_i
【輸出格式】
第1行: 輸出1個整數,表示使所有奶牛滿意的最小花費。如果無論如何都無法滿足所有奶牛的需求,輸出-1
【輸入樣例】
4 7
1 1
2 3
1 4
4 2
3 2
2 1
4 3
5 2
5 4
2 6
4 4
【輸出樣例】
12
【樣例解釋】
給奶牛1吃價錢爲2的2號牧草,奶牛2吃價錢爲4的3號牧草,奶牛3分到價錢爲2的6號牧草,奶牛4選擇價錢爲4的7號牧草,這種分配方案的總花費是12,爲所有方案中花費最少的。
【數據範圍】
1<=N,M<=100,000
1 <= A_i,C_i,D_i<=1,000,000,000
【來源】
poj 3622
做題思路(超時解法):拿到這道題時,首先想到的就是貪心,對於每頭奶牛應從滿足它要求的價錢和鮮嫩程度的牧草中,選擇價錢和鮮嫩程度都最小的牧草。在實現貪心算法時,先將牧草按價錢由小到大排序,將奶牛按要求的鮮嫩程度由小到大排序,然後依次枚舉牧草,每枚舉一株牧草,在奶牛中查找要求的鮮嫩程度小於等於該牧草的鮮嫩程度的第一頭奶牛,如果該奶牛還未吃到牧草且它要求的價錢小於等於該牧草的價錢,則該牧草就給它吃,否則就看前一頭奶牛。如果最後吃到草的奶牛數小於總數,則輸出-1。這樣的做法,在查找奶牛時耗費的時間較多,對於大數據會超時。
解題思路(正解):在實現貪心算法時,可以先將牧草和奶牛都按鮮嫩程度和要求的鮮嫩程度由大到小排序,建立一個多重集合(mulitset),用於存牧草的價格。每枚舉一頭奶牛,先將滿足它要求的鮮嫩程度的牧草的價格存進多重集中,然後在多重集中查找牧草價格大於等於它要求的價格的第一個牧草,如果沒有,則輸出-1,並跳出循環;如果有,就將答案加上該價格,並將該價格刪去一個,最後的答案即爲滿足奶牛的最小花費。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
const int maxn=100005;
int N,M;
struct data1
{
int a,b;
};
data1 aa[maxn]; //記錄奶牛
struct data2
{
int c,d;
};
data2 bb[maxn]; //記錄牧草
bool cmp1(data2 cc,data2 dd)
{
return cc.d>dd.d;
}
bool cmp2(data1 cc,data1 dd)
{
return cc.b>dd.b;
}
multiset<int>mt; //記錄牧草價格
multiset<int>::iterator it;
int main()
{
freopen("cate.in","r",stdin);
//freopen("cate.out","w",stdout);
scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++)
scanf("%d%d",&aa[i].a,&aa[i].b);
for(int j=1;j<=M;j++)
scanf("%d%d",&bb[j].c,&bb[j].d);
sort(bb+1,bb+1+M,cmp1); //按牧草的鮮嫩程度由大到小排序
sort(aa+1,aa+1+N,cmp2); //按奶牛要求的鮮嫩程度由大到小排序
int ok=1,j=1;
long long ans=0;
for(int i=1;i<=N;i++)
{
while(bb[j].d>=aa[i].b) //將滿足要求的牧草的價格存進多重集
{
mt.insert(bb[j].c);
j++;
}
it=mt.lower_bound(aa[i].a); //找第一株價格大於等於奶牛要求的牧草
if(it==mt.end()) //沒有牧草滿足要求
{
ok=0;
printf("-1\n");
break;
}
ans+=*it;
mt.erase(it); //注意刪除時只能刪除該地址的元素,不能直接刪除元素的值(*it)
}
if(ok==1) cout<<ans<<'\n';
return 0;
}