03-贪心

1、简单贪心

1319:【例6.1】排队接水

【题目描述】

有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,请编程找出这n个人排队的一种顺序,使得n个人的平均等待时间最小。

【输入】

共两行,第一行为n(1≤n≤1000);第二行分别表示第1个人到第n个人每人的接水时间T1,T2,…,Tn,每个数据之间有1个空格。

【输出】

有两行,第一行为一种排队顺序,即1到n的一种排列;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。

【输入样例】

10                                            

56 12 1 99 1000 234 33 55 99 812

【输出样例】

3 2 7 8 1 4 9 6 10 5

291.90

<代码>

#include<bits/stdc++.h>
using namespace std;
int n,sum;
struct node
{
    int id,v;
	bool operator <(const node &x)const
	{
		return v<x.v;
	}
}a[1005];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i].v;
		a[i].id=i;
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		sum+=a[i-1].v,a[i].v+=a[i-1].v;//前缀和
		cout<<a[i].id<<' ';
	}
	cout<<endl;
	printf("%.2lf",1.0*sum/n);
	return 0;
}

2、贪心模型转化

1320:【例6.2】均分纸牌(Noip2002)

【题目描述】

有n堆纸牌,编号分别为 1,2,…,n。每堆上有若干张,但纸牌总数必为n的倍数。可以在任一堆上取若干张纸牌,然后移动。

移牌规则为:在编号为1的堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 n 的堆上取的纸牌,只能移到编号为n−1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

例如 n=4,4堆纸牌数分别为:  ① 9 ② 8 ③ 17 ④ 6

移动3次可达到目的:

从 ③ 取4张牌放到④(9 8 13 10)->从③取3张牌放到 ②(9 11 10 10)-> 从②取1张牌放到①(10 10 10 10)。

【输入】

n(n 堆纸牌,1≤n≤100)

a1a2…an(n 堆纸牌,每堆纸牌初始数,l≤ai≤10000)。

【输出】

所有堆均达到相等时的最少移动次数。

【输入样例】

4

9 8 17 6

【输出样例】

3

<代码

#include<bits/stdc++.h>
using namespace std;
int ans,a[1001],sum,n;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum+=a[i];
	}
	sum/=n;
	for(int i=1;i<=n;i++)
	{
		if(a[i]==sum)continue;
		ans++;
		a[i+1]+=a[i]-sum;
	}
	cout<<ans<<endl;
	return 0;
}

3、下降序列覆盖

1322:【例6.4】拦截导弹问题(Noip1999)

【题目描述】

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统,但是这种拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,由于该系统还在试用阶段。所以一套系统有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度不大于30000的正整数)。计算要拦截所有导弹最小需要配备多少套这种导弹拦截系统。

【输入】

n颗依次飞来的高度(1≤n≤1000)。

【输出】

要拦截所有导弹最小配备的系统数k。

【输入样例】

389 207 155 300 299 170 158 65

【输出样例】

2

【提示】

输入:导弹高度: 4  3  2

输出:导弹拦截系统k=1

<代码>

#include<bits/stdc++.h>
using namespace std;
int n,x,a[1001];
int main()
{
	while(cin>>x)
	{
		int k=0;
		for(int i=1;i<=n;i++)
		  if(a[i]>=x)
		  {
			k=i;break;
		  }
		if(k==0)k=++n;
		a[k]=x;
	}
	cout<<n<<endl;
	return 0;
}

4、不相交区间

1323:【例6.5】活动选择

【题目描述】

学校在最近几天有n个活动,这些活动都需要使用学校的大礼堂,在同一时间,礼堂只能被一个活动使用。由于有些活动时间上有冲突,学校办公室人员只好让一些活动放弃使用礼堂而使用其他教室。

现在给出n个活动使用礼堂的起始时间begini和结束时间endi(begini<endi),请你帮助办公室人员安排一些活动来使用礼堂,要求安排的活动尽量多。

【输入】

第一行一个整数n(n≤1000);

接下来的n行,每行两个整数,第一个begini,第二个是endi(begini<endi≤32767)。

【输出】

输出最多能安排的活动个数。

【输入样例】

11

3 5

1 4

12 14

8 12

0 6

8 11

6 10

5 7

3 8

5 9

2 13

【输出样例】

4

<代码>

#include<bits/stdc++.h>
using namespace std;
int n,tmp,cnt;
struct node
{
    int st,ed;
    bool operator <(const node& x)const
    {
		return ed<x.ed;
	}
}a[1100];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i].st>>a[i].ed;
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		if(tmp<=a[i].st)cnt++,tmp=a[i].ed;
	}
	cout<<cnt<<endl;
	return 0;
}

5、区间覆盖

1424:【例题3】喷水装置

【题目描述】

长 L米,宽 W 米的草坪里装有 n 个浇灌喷头。每个喷头都装在草坪中心线上(离两边各 W/2米)。我们知道每个喷头的位置(离草坪中心线左端的距离),以及它能覆盖到的浇灌范围。

 

 

 

请问:如果要同时浇灌整块草坪,最少需要打开多少个喷头?

【输入】

输入包含若干组测试数据。

第一行一个整数 T

表示数据组数;

每组数据的第一行是整数 n、L 和 W;

接下来的 n行,每行包含两个整数,给出一个喷头的位置和浇灌半径(上面的示意图是样例输入第一组数据所描述的情况)。

【输出】

对每组测试数据输出一个数字,表示要浇灌整块草坪所需喷头数目的最小值。如果所有喷头都打开也不能浇灌整块草坪,则输出 −1。

【输入样例】

3

8 20 2

5 3

4 1

1 2

7 2

10 2

13 3

16 2

19 4

3 10 1

3 5

9 3

6 1

3 10 1

5 3

1 1

9 1

【输出样例】

6

2

-1

【提示】

数据范围:

对于 100% 的数据,n≤15000。

<代码>

#include<bits/stdc++.h>
using namespace std;
int n,t,m;
double l,w;
struct node
{
    double st,ed;
    bool operator <(const node& x)const
    {
		return st<x.st;
	}
}a[15010];
double getdis(double r,double h)
{
	return sqrt(r*r-h*h);
}
void work()
{
	int ans=0;
	double tmp=0;
	for(int i=1;i<=m;i++)
	{
		double mx=tmp;
		if(tmp>=a[i].st)//找一个能接的上的最远的
		{
			while(tmp>=a[i].st&&i<=m)mx=max(mx,a[i].ed),i++;
			i--;
			ans++;
			tmp=mx;
			if(tmp>=l)break;
		}
		else {cout<<-1<<endl;return;}
		if(i==m&&tmp<l){cout<<-1<<endl;return;}
	}
	cout<<ans<<endl;
}
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n>>l>>w;
		m=0;
		w/=2;
		for(int i=1;i<=n;i++)
		{
			double pos,r;
			scanf("%lf%lf",&pos,&r);
			if(r<=w)continue;
			r=getdis(r,w);
			a[++m]=(node){pos-r,pos+r};//覆盖范围
		}
		sort(a+1,a+m+1);
		work();
	}
	return 0;
}

6、区间选点

1324:【例6.6】整数区间

【题目描述】

请编程完成以下任务:

1.读取闭区间的个数及它们的描述;

2.找到一个含元素个数最少的集合,使得对于每一个区间,都至少有一个整数属于该集合,输出该集合的元素个数。

【输入】

首行包括区间的数目n,1≤n≤10000,接下来的n行,每行包括两个整数a,b,被一空格隔开,0≤a≤b≤10000,它们是某一个区间的开始值和结束值。

【输出】

第一行集合元素的个数,对于每一个区间都至少有一个整数属于该集合,且集合所包含元素数目最少。

【输入样例】

4

3 6

2 4

0 2

4 7

【输出样例】

2

<代码>

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct node
{
	int st,ed;
	bool operator<(const node x)const
	{
		return ed<x.ed;
	}
}a[N];
int n,ans,tmp=-1;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		scanf("%d%d",&a[i].st,&a[i].ed);
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		if(tmp>=a[i].st)continue;
		ans++;
		tmp=a[i].ed;
	}
	cout<<ans<<endl;
	return 0;
}

7、区间合并

1236:区间合并

【题目描述】

给定 n个闭区间 [ai,bi],其中i=1,2,...,n。任意两个相邻或相交的闭区间可以合并为一个闭区间。例如,[1,2] 和 [2,3] 可以合并为 [1,3],[1,3] 和 [2,4] 可以合并为 [1,4],但是[1,2] 和 [3,4] 不可以合并。

我们的任务是判断这些区间是否可以最终合并为一个闭区间,如果可以,将这个闭区间输出,否则输出no。

【输入】

第一行为一个整数n,3≤n≤50000。表示输入区间的数量。

之后n行,在第i行上(1≤i≤n),为两个整数 ai 和 bi ,整数之间用一个空格分隔,表示区间 [ai,bi](其中 1≤ai≤bi≤10000)。

【输出】

输出一行,如果这些区间最终可以合并为一个闭区间,输出这个闭区间的左右边界,用单个空格隔开;否则输出 no。

【输入样例】

5

5 6

1 5

10 10

6 9

8 10

【输出样例】

1 10

<代码>

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
struct node
{
	int l,r;
	bool operator<(const node &x)const
	{
		return l<x.l;
	}
}a[N];
int n,st,ed;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		scanf("%d%d",&a[i].l,&a[i].r);
	sort(a+1,a+n+1);
	st=a[1].l,ed=a[1].r;
	for(int i=2;i<=n;i++)
	{
		if(ed<a[i].l){cout<<"no";return 0;}
        ed=max(ed,a[i].r);
	}
	cout<<st<<' '<<ed<<endl;
    return 0;
}

8、贪心之和

P1115 最大子段和

题目描述

给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。

【输入格式】

第一行是一个整数,表示序列的长度 n。

第二行有 n 个整数,第 i 个整数表示序列的第 i 个数字 ai ​。

【输出格式】

输出一行一个整数表示答案。

输入输出样例

输入 #1

7

2  -4  3  -1  2  -4  3

输出 #1

4

说明/提示

样例 1 解释

选取 [3,5]子段 {3,−1,2},其和为 4。

数据规模与约定

对于 40% 的数据,保证 n≤2×1e3。

对于 100% 的数据,保证 1≤n≤2×1e5,−1e4≤ai≤1e4。

<代码>

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int a,n,sum,mx=-2e9;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);
		sum+=a;
		mx=max(mx,sum);
		if(sum<0)sum=0;
	}  
	cout<<mx;
	return 0;
}

9、部分背包

1225:金银岛

【题目描述】

某天KID利用飞行器飞到了一个金银岛上,上面有许多珍贵的金属,KID虽然更喜欢各种宝石的艺术品,可是也不拒绝这样珍贵的金属。但是他只带着一个口袋,口袋至多只能装重量为w的物品。岛上金属有s个种类, 每种金属重量不同,分别为n1,n2,...,ns,同时每个种类的金属总的价值也不同,分别为v1,v2,...,vs。KID想一次带走价值尽可能多的金属,问他最多能带走价值多少的金属。注意到金属是可以被任意分割的,并且金属的价值和其重量成正比。

【输入】

第1行是测试数据的组数k,后面跟着k组输入。

每组测试数据占3行,第1行是一个正整数w(1≤w≤10000),表示口袋承重上限。第2行是一个正整数s(1≤s≤100),表示金属种类。第3行有2s个正整数,分别为n1,v1,n2,v2,...,ns,vs分别为第一种,第二种,...,第s种金属的总重量和总价值(1≤ni≤10000,1≤vi≤10000)。

【输出】

k行,每行输出对应一个输入。输出应精确到小数点后2位。

【输入样例】

2

50

4

10 100 50 30 7 34 87 100

10000

5

1 43 43 323 35 45 43 54 87 43

【输出样例】

171.93

508.00

<代码>

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
struct node
{
	double s,v,p;
	bool operator<(const node& x)const
	{
		return p>x.p;
	}
}a[N];
int k,n;
double tmp,w,ans;
int main()
{
	cin>>k;
	while(k--)
	{
		cin>>w>>n;
		for(int i=1;i<=n;i++)
		{
			scanf("%lf%lf",&a[i].s,&a[i].v);
			a[i].p=a[i].v/a[i].s;//性价比
		}  
		sort(a+1,a+n+1);
	    ans=tmp=0;
		for(int i=1;i<=n;i++)
		{
			ans+=min(w-tmp,a[i].s)*a[i].p;
			tmp+=min(w-tmp,a[i].s);
			if(tmp>=w)break;
		}
		printf("%.2lf\n",ans);
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值