1. 程式人生 > >【POJ3614】【USACO 2007 Nov Gold】 3.Sunscreen 貪心

【POJ3614】【USACO 2007 Nov Gold】 3.Sunscreen 貪心

題意:

有若干個區間,若干種數,每個數告訴你有多少個。

然後一個數可以被放到一個x∈該區間 的區間,問最多有多少個區間可以被放。

題解:

顯然我們可以用二分圖最大匹配做,水題。

但是此題有別的技巧、

就是我們可以貪心進行處理。

首先我們考慮到需要將兩種數都排個序。

然後再進行貪心。

一種錯誤的貪心法是單調佇列式貪心,就是記錄個top,然後單調往後推。

這個不仔細想還不知道它是錯的。

額,至於卡它的資料,,我可以提供給你一個錯誤程式碼和一個拍子,你們可以自己拍一組資料出來,很高效。

這裡貼一份錯誤程式碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 3000
#define inf 0x3f3f3f3f
using namespace std;
int n,m,ans,i;
struct KSD
{
	int x,y;
	bool operator < (const KSD &a)const{return x==a.x?(y<a.y):(x<a.x);}
}cow[N],oil[N];
int main()
{
//  freopen("test.in","r",stdin);
	scanf("%d%d,",&n,&m);
	for(i=1;i<=n;i++)scanf("%d%d",&cow[i].x,&cow[i].y);
	for(i=1;i<=m;i++)scanf("%d%d",&oil[i].x,&oil[i].y);
	sort(cow+1,cow+n+1);
	sort(oil+1,oil+m+1);
	int top=1;
	for(i=1;i<=m;i++)
	{
		while(top<=n&&cow[top].y<oil[i].x)top++;
		while(top<=n&&oil[i].y&&cow[top].x<=oil[i].x)
		{
			if(oil[i].x<=cow[top].y)
			{
				oil[i].y--;
				ans++;
			}
			top++;
		}
	}
	printf("%d\n",ans);
	return 0;
}

這裡貼一份資料生成器

#include <ctime>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 10
#define inf 0x3f3f3f3f
using namespace std;
int n,m;
int main()
{
	srand((unsigned)time(NULL));
	int i,j,k;
	int a,b,c;
	n=rand()%N+1;
	m=rand()%4+1;
	printf("%d %d\n",n,m);
	for(i=1;i<=n;i++)
	{
		a=rand()%N+1;
		b=rand()%N+a;
		printf("%d %d\n",a,b);
	}
	for(i=1;i<=m;i++)
	{
		a=rand()%(N*2)+1;
		b=rand()%3+1;
		printf("%d %d\n",a,b);
	}
	return 0;
}

這裡貼個拍子

#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 3000
using namespace std;

int main()
{
	int i,g;
	system("g++ std.cpp -o std -g");
	system("g++ my.cpp -o my -g");
	system("g++ rand.cpp -o rand -g");
	for(g=1;g<=N;g++)
	{
		printf("Case %d :   ",g);
		system("rand>test.in");
		clock_t j=clock();
		system("my <test.in >my.out");
		clock_t k=clock();
		printf("my_use : %03dms  ",k-j);
		system("std <test.in >std.out");
		printf("std_use : %03dms  ",clock()-k);
		if(system("fc std.out my.out >NULL")==0){puts("result : AC");}
		else 
		{
			puts("WAWAWAWAWAWAWAWA!!!!!!!!");
			system("start test.in");
			return 0;
		}
	}
	puts("");
	puts("****,please try again");
	return 0;
}

拿上述的東西,再找個標程就可以pai了。

下面說一下正確思路。

首先按照順序我們可以知道哪些區間的下界滿足條件,那麼就只需要上界也滿足條件就行了。

因為數遞增,所以如果當前的某個可用區間的上界比當前數小,那麼以後也不可能滿足了,就可以彈出去。

這樣通過優先佇列可以得到一個上界滿足且最小的區間,這個區間則可以被計入答案。

思想說了個大概,沒懂就看程式碼吧。

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 3000
#define inf 0x3f3f3f3f
using namespace std;
int n,m,ans,i;
priority_queue<int>pq;
struct KSD
{
	int x,y;
	bool operator < (const KSD &a)const{return x==a.x?(y<a.y):(x<a.x);}
}cow[N],oil[N];
int main()
{
//	freopen("test.in","r",stdin);
	scanf("%d%d,",&n,&m);
	for(i=1;i<=n;i++)scanf("%d%d",&cow[i].x,&cow[i].y);
	for(i=1;i<=m;i++)scanf("%d%d",&oil[i].x,&oil[i].y);
	sort(cow+1,cow+n+1);
	sort(oil+1,oil+m+1);
	int top=1;
	for(i=1;i<=m;i++)
	{
		while(top<=n&&cow[top].x<=oil[i].x)
			pq.push(-1*cow[top++].y);
		while(!pq.empty()&&oil[i].y)
		{
			int ttt=pq.top();
			if((-1*ttt)>=oil[i].x)ans++,oil[i].y--;
			pq.pop();
		}
	}
	printf("%d\n",ans);
	return 0;
}