【集训day1】贪心(4/11)

Day1 贪心

A:
给定一串字母,每次从该字母串的头或尾取一个字母加入到新串中,要求所得新串字典序最小

思路:
显然,每次直接比较头部和尾部字母就好,头尾的指针在取字母的时候更新。
考虑首尾字母相同的情况,此时需要比较第二个和倒数第二个字母,如果仍相同,则继续比较下一个,所以有

for(int i=0;i+a<=b;i++){
	if(p[a+i]<p[b-i])

只要两字母不再相同就可以break跳出,取字母

#include<stdio.h>
int p[2010];
int q[2010];
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf(" %c",&p[i]);
	}
	int a=1,b=n;
	int pos=0;
	while(a<=b){
		bool left=false;
		for(int i=0;i+a<=b;i++){
			if(p[a+i]<p[b-i]){
				left = true;
				break;
			}
			//下面这个else if如果删去会出错
			else if(p[a+i]>p[b-i]){
				left = false;
				break;
			}
		}
		if(left) q[++pos]=p[a++];
		else q[++pos]=p[b--]; 
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		printf("%c",q[i]);
		ans++;
		if(ans==80){
			printf("\n");
			ans=0;
		}
	}
	return 0;
 } 

B:
有N个点,其中一些点插眼,眼的作用距离为R。如,插在点x=11上的距离为10的眼,最远可以作用到x=1与x=21的点。
求能使所有点被监视的最少插眼数目

又读错题了orz,以为是在数轴上任意地方插眼,原来是必须在点上插眼orz

#include<stdio.h>
#include<algorithm>
using namespace std;
 
int main()
{
	int a[1006];
	int r,n;
	while(scanf("%d%d",&r,&n)==2){
		if(r==-1&&n==-1) break;
		for(int j=1;j<=n;j++) scanf("%d",&a[j]);
		sort(a+1,a+n+1);
		int ans=0,i=1; 
		while(i<=n){
			int s=a[i];//以未被监视的点中最左边的那一个作为起点 
			while(i<=n&&a[i]<=s+r) i++;//一直前进,直到当前点超出了监视距离 
			int p=a[i-1];
			ans++;//给前一个点打标记,这样才能监视到起点 
			while(i<=n&&a[i]<=p+r) i++;//从监视点的下一个点继续出发,直到当前点超出了监视距离 
		}
		printf("%d\n",ans);	
	}
	return 0;
}

D:
有T个工作时间段,且有N头给定工作时间的牛,要求:雇佣尽量少的牛使得1-T时间段内都至少有一头牛工作。求这个最小数量。

贪心算法是:将所有牛按起始时间从早到晚排序,每次都挑可接班的牛中结束时间最晚的。
(这个怎么证啊啊啊啊啊啊啊)

有个坑:1-4,5-10也是连续的从1到10时间段的选择,不一定要1-4,4-10.

#include<stdio.h>
#include<algorithm>
using namespace std;
struct str_cow{
	int begin;
	int end;
}cow[25005];
bool cmp(str_cow a,str_cow b){
	if(a.begin==b.begin) return a.end<b.end;
	else return a.begin<b.begin;
}
int main()
{
	int n,t;
	scanf("%d%d",&n,&t);
	for(int i=1;i<=n;i++) scanf("%d%d",&cow[i].begin,&cow[i].end);
	sort(cow+1,cow+n+1,cmp);
	//排序,最先开始的排在前面,如果同一时间开始,则结束最早的排在前面 
	int tail1=-1;
	for(int i=1;i<=n;i++){
		if(cow[i].begin!=1) break;
		if(cow[i].end>tail1) tail1=cow[i].end;
	}
	//牛的最晚接班时间 
	int ans=1;
	//雇佣牛的数目 
	int tail2=-1; 
	int ok=1;
	while(tail1<t&&ok==1){//只要当前选好的结束时间还没达到要求的结束时间,就继续选下一头牛 
		ok=0;//先假设这里开始接不上 
		for(int i=1;i<=n;i++){ 
			if (cow[i].begin>tail1+1) break;
			//如果第i头牛的开始时间比接班时间要晚,接不上,后面的也接不上 
			//在所有开始时间小于当前接班时间的牛中找结束时间最晚的那一个 
			else if(cow[i].end>tail2) {
				ok=1; 
				tail2=cow[i].end;
			//否则,记下第i头牛的结束时间,如果结束时间要更晚,就选用 
			}
		}
		tail1=tail2;
		ans++;//选用数+1 
	}
	if(ok==0) printf("-1");
	else printf("%d",ans);
	return 0;
}

E:
给定一些点的坐标(保证y>0)及圆的半径,为保证这些点能被若干个圆心在x轴上的圆全部覆盖,求需要圆的最少数目。如果不能全部覆盖,输出-1

思路1(也许是错的,因为A不掉,原因未知):
将点按照x坐标从小到大排序。每次考虑第一个未被雷达覆盖到的点,用d²-y²得出恰好覆盖该点的圆的圆心所在。然后向右遍历各点,计算与该圆心的距离以确认是否被覆盖,若出现没有覆盖的点,重复上述步骤,直到已经计算过最后一个点。

思路2:
假设雷达就放在岛上,则当岛可以在原来情况被覆盖时,雷达的范围应与x轴有交点,将交点作为这个岛的左右边界。显然,当岛与岛之间左右边界有重叠部分时,两岛可以共用一个雷达。

错误分析:
从昨天写到现在,就差照着题解码了……

问题一:边界排序
毫无疑问是将右边界从小到大排,但是没有考虑到右边界相等时的情况。
右边界相等时,应将左边界从大到小排,确保区间大的在前面。

问题二:数据类型
一开始d写成了int
最大右边界变量right一开始也是int,A得过才有鬼咯

问题三:雷达初始值问题
这里雷达初始值应为1,既可以保证n=1时有1个雷达,又符合n>1时的情况

#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
struct str_island{
	float x;
	float y;
	float round_l;
	float round_r;
}island[1005];

bool cmp(str_island a,str_island b){
	if(a.round_r==b.round_r) return a.round_l>b.round_l;
	return a.round_r<b.round_r;
} 

int main()
{
	int kase=0;
	float d;
	//类型啊!!一开始是int 
	int n;
	while(scanf("%d%f",&n,&d)==2&&n&&d){
		int radar=0;
		for(int i=1;i<=n;i++) scanf("%f%f",&island[i].x,&island[i].y);
		//载入小岛坐标
		int ok=1;
		for(int i=1;i<=n;i++){
			if(d>=island[i].y){
				//对于这个小岛,假设雷达在岛上,则范围与x轴应有交点,算出左右端点 
				island[i].round_l=island[i].x - sqrt(d * d - island[i].y * island[i].y);//左端点 
				island[i].round_r=island[i].x + sqrt(d * d - island[i].y * island[i].y);//右端点 
			}
			else{
				printf("Case %d: -1\n",++kase);
				ok=0;
				break;
				//有一个岛不能被覆盖,直接跳出 
			}
		}
		if(ok==0) continue;
		else{
			radar=1;
			//注意雷达的初始数量
			//考虑到下面的计算步骤以及只有一个岛的情况
			//这里应该一开始就放了一个雷达
			sort(island+1,island+n+1,cmp);
			//将小岛覆盖区间的右端点从小到大排序
			float right=island[1].round_r;
			//注意类型,这里一开始写的是int 
			for(int i=2;i<=n;i++){
				if(island[i].round_l>right){
					radar++;
					//超出了上一个雷达的覆盖范围,
					right=island[i].round_r;
					//新放了个雷达,故更新边界
				}
			}
			printf("Case %d: %d\n",++kase,radar);
		}

	}
	return 0;
}
发布了28 篇原创文章 · 获赞 0 · 访问量 666

猜你喜欢

转载自blog.csdn.net/weixin_45561591/article/details/103924831