首页 > 代码库 > 【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;
}



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