贪心算法合集1

目录

合并果子 

奶牛玩杂技 

排队接水

接水问题

修理牛棚


贪心算法

1.是一种基于贪心策略的算法,它在每一步选择中都采取当前状态下最优的选择,以希望最终能够   获得全局最优解。

2.贪心算法通常适用于求解最优化问题,但不能保证能够得到全局最优解,只能得到近似最优解。

3.核心思想是:每一步都选择当前状态下的最优解,不考虑之后的选择可能带来的影响。

试手题

合并果子 

P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述:


在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 33 种果子,数目依次为 1 , 2 , 9 。可以先将 1 、 2 堆合并,新堆数目为 3 ,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 ,耗费体力为 12 。所以多多总共耗费体力 =3+12=15 。可以证明 15 为最小的体力耗费值。

输入格式

共两行。
第一行是一个整数n(1≤n≤10000) , ,表示果子的种类数。
第二行包含 n 个整数,用空格分隔,第 i 个整数ai(1≤ai≤20000) 是第i种果子的数目。

输出格式

一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 231。

输入输出样例:
输入样例1:

3
1 2 9

输出样例1:

15

 有点类似哈夫曼树,每次取最小俩个合并,并放回原集合中,我们只需在放回的过程中记录他们合并的值,就能实现,最小合并;

#include<iostream>
#include<queue>
using namespace std;
//构建小顶堆 
priority_queue<int,vector<int>,greater<int> >q;
int main(){
	int n;
	cin>>n;
	while(n--){
		int x;
		cin>>x;
		q.push(x);
	}
	int ans=0;
//q.size()==1时表示合并结束
	while(q.size()!=1){
//		每次把集合内最小的俩个取出来记录相加的值,并重新放入小顶堆中 
		int x=q.top();q.pop();
		int y=q.top();q.pop();
		ans+=(x+y);
		q.push((x+y));
	}
	cout<<ans<<endl;
	return 0;
} 

奶牛玩杂技 

P1842 [USACO05NOV] 奶牛玩杂技 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

每头牛都有自己的体重以及力量,编号为 i 的奶牛的体重为 Wi​,力量为 Si​。

当某头牛身上站着另一些牛时它就会在一定程度上被压扁,我们不妨把它被压扁的程度叫做它的压扁指数。对于任意的牛,她的压扁指数等于摞在她上面的所有奶牛的总重(当然不包括她自己)减去它的力量。奶牛们按照一定的顺序摞在一起后, 她们的总压扁指数就是被压得最扁的那头奶牛的压扁指数。

你的任务就是帮助奶牛们找出一个摞在一起的顺序,使得总压扁指数最小。

输入描述

第一行输入整数N,表示奶牛数量。
接下来N行,每行输入两个整数,表示牛的重量和强壮程度,第 i 行表示第 i 头牛的重量 Wi 以及它的强壮程度 Si。

输出描述

输出一个整数,表示最大风险值的最小可能值。

数据范围

1≤ N ≤50000,
1≤ Wi ≤10,000,
1≤ Si ≤1,000,000,000

输入样例

3
10 3
2 5
3 3

输出样例

2
 

假如考虑重量最大放在最下,忽略了力量,一定不是最优解,同理只考虑力量,也不行

要考虑2个因素力量和重量,不妨假设是由力量+重量之和排序,枚举

假设i头牛按照W+S排序由小到大            假如把最后一头牛和倒数第二头牛交换位置

     W1+S1                                                      W1+S1             

     W2+S2,                                                     W2+S2,     

     ..........                                                       ..........                   

   W(i-1)+S(i-1)                                               W(i)+S(i)                         

    W(i)+S(i)                                                   W(i-1)+S(i-1)                    

ans1=W1+....+W(i-1)-S(i)                        ans2=W1+....W(i)-S(i-1)

由于排序    W(i)+S(i)   >   W(i-1)+S(i-1) 

 变式          W(i)-S(i-1)>W(i-1)-S(i)

则              ans2>ans1

ans1才是最小风险 

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e5+10;
struct node{
	int w,s;
}a[N];
//写排序规则,对力量+重量排序由小到大
bool cmp(struct node a,struct node b){
	return (a.s+a.w)<(b.s+b.w);
}

int main(){
    int n;cin>>n;
    for(int i=0;i<n;++i){
    	cin>>a[i].w>>a[i].s;
    }
    sort(a,a+n,cmp);
//别取小了,小了过不了
    int ans=-0x3f3f3f3f3f;w=0;
    for(int i=0;i<n;++i){
//寻找哪个牛压扁指数最大
    	ans=max(ans,w-a[i].s);
		w+=a[i].w;
    }
    cout<<ans<<endl;
} 

排队接水

P1223 排队接水 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

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

输入格式

第一行为一个整数 n。

第二行 n 个整数,第 i 个整数 Ti​ 表示第 i 个人的等待时间 Ti​。

输出格式

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

输入

10

56 12 1 99 1000 234 33 55 99 812

输出

3 2 7 8 1 4 9 6 10 5

291.90

 思路:时间最小的排在最前面,假设按照这个要求排序

等待时间是除自己以外的人等自己接水的时间,诺我是第i个人我后面人等待我接水的时间总和为(n-i)*t(i)

接水时间 :t1,t2...t(i),t(j)

等待时间:T1=(n-1)*t1+(n-2)*t2+...+(n-i)*t(i)+(n-j)*t(j)+....

诺交换i,j顺序

等待时间:T2=(n-1)*t1+(n-2)*t2+...+(n-i)*t(j)+(n-j)*t(i)+....

T1-T2=(n-i)*t(i)+(n-j)*t(j)-(n-i)*t(j)+(n-j)*t(i)

        =(n-i)( t(i) - t(j) )+(n-j)( t(j) - t(i) )

        =( t(j)-t(i) )( i-j )

        <0

T1<T2

证明该方法最优

#include<iostream>
#include<cstring>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int N=1e4;
//结构体放入时间以及下标
struct node{
	int t,id;
}a[N];
//按照时间进行排序
bool cmp(struct node a,struct node b){
	return a.t<b.t;
}

int main(){
   int n;cin>>n;
   for(int i=1;i<=n;++i){
   	cin>>a[i].t;
   	a[i].id=i;
   }
   sort(a+1,a+n+1,cmp);
   for(int i=1;i<=n;++i){
   	cout<<a[i].id<<' ';
   }
   cout<<endl;
   double time=0;
   for(int i=1;i<=n;++i){
//求总的等待时间
   	time+=(n-i)*a[i].t;
   }
   printf("%.2lf",time/n);
} 

接水问题

P1190 [NOIP2010 普及组] 接水问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述:

学校里有一个水房,水房里一共装有 m 个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为 1。

现在有 n 名同学准备接水,他们的初始接水顺序已经确定。将这些同学按接水顺序从 1 到 n 编号,i号同学的接水量为 wi。接水开始时,1 到 m 号同学各占一个水龙头,并同时打开水龙头接水。当其中某名同学 j 完成其接水量要求 wj后,下一名排队等候接水的同学 k 马上接替 j 同学的位置开始接水。这个换人的过程是瞬间完成的,且没有任何水的浪费。即 j 同学第 x 秒结束时完成接水,则 k 同学第 x+1 秒立刻开始接水。 若当前接水人数 n’不足 m,则只有 n’个龙头供水,其它 m-n’个龙头关闭。

现在给出 n 名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。

输入格式:

第 1 行2 个整数 n 和 m,用一个空格隔开,分别表示接水人数和龙头个数。
第 2 行 n 个整数 w1、w2、……、wn,每两个整数之间用一个空格隔开,wi表示 i 号同学的接水量。

1 ≤ n ≤ 10000,1 ≤ m ≤ 100 且 m ≤ n;
1 ≤ wi ≤ 100。

输出格式:

输出只有一行,1 个整数,表示接水所需的总时间。

样例输入:

样例 #1:
5 3
4 4 1 2 1

样例 #2:
8 4
23 71 87 32 70 93 80 76

样例输出:

样例 #1:
4

样例 #2:
163

案例1中观察可知,先按照顺序让同学接水,水接完换同学,判断方法:哪个数小,挂在哪个水龙头下,起始放入 4 4 1,3号水龙头数值小,把2挂在3号下,3号变为 3,3号水龙头还是最小,把1挂在3号下,

水龙头 1 2 3

             4 4 1

             4 4 3    2挂在3号

             4 4 4   1挂在3号

结果取最大,即接水总时间

#include<iostream>
#include<cstring>
#include<algorithm>
#include<stdio.h>
using namespace std;
int a[100000];
int w[100000];
int main(){
   int n,m;cin>>n>>m;
//  存入每个同学接水时间 
   for(int i=0;i<n;++i)cin>>w[i];
   for(int i=0;i<n;++i){
//  从第一个水龙头找哪个水龙头数值最小 
   	int first=0;
   	for(int j=1;j<m;++j){
//  找到赋值给first 		
   		if(a[j]<a[first])first=j;
   	}
//把值挂在数值最小的水龙头上   	
   	a[first]+=w[i];
   }
//   排序取最大值,即为总时间 
   sort(a,a+n);
   cout<<a[n-1]<<endl;
} 

修理牛棚

P1209 [USACO1.3] 修理牛棚 Barn Repair - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

在一个月黑风高的暴风雨夜,Farmer John 的牛棚的屋顶、门被吹飞了 好在许多牛正在度假,所以牛棚没有住满。

牛棚一个紧挨着另一个被排成一行,牛就住在里面过夜。有些牛棚里有牛,有些没有。 所有的牛棚有相同的宽度。

自门遗失以后,Farmer John 必须尽快在牛棚之前竖立起新的木板。他的新木材供应商将会供应他任何他想要的长度,但是吝啬的供应商只能提供有限数目的木板。 Farmer John 想将他购买的木板总长度减到最少。

给出 m,s,c,表示木板最大的数目、牛棚的总数、牛的总数;以及每头牛所在牛棚的编号,请算出拦住所有有牛的牛棚所需木板的最小总长度。

输入格式

一行三个整数 m,s,c,意义如题目描述。
接下来 c 行,每行包含一个整数,表示牛所占的牛棚的编号。

输出格式

输出一行一个整数,表示所需木板的最小总长度。

输入输出样例
输入
4 50 18
3 
4 
6 
8 
14
15 
16 
17 
21
25 
26 
27 
30 
31 
40 
41 
42 
43

 输出

25

 这道题最优思路是:如果s<m,给每头牛修一个木板,则s为最小总长度,若s>m,木板数不够,先给每个牛一个木板(木板数为c),对牛的编号排序,把最近的牛合并在一个木板,把合并的长度排序,选择合并长度最小的,直到木板数=m,结合次数=c-m。

这么做的原因:每个牛起始给一块木板,共用c块木板,条件需要m块木板,多出m-c块木板,就需要让牛棚之间合并木板,减少木板数目,直到数目=m,牛棚之间怎么合并,选取距离最近的牛棚合并,长度可以最小,再对长度排序,选取最短长度

#include<iostream>
#include<cstring>
#include<algorithm>
#include<stdio.h>
using namespace std;
//存储牛棚编号 
int a[100000];
//存储最近牛棚之间的距离-1,合并需要的长度
//2,3合并直接合并不需要额外长度(3-2-1)
//2,4合并(4-2-1),合并需要长度为1 
int d[100000];
int main(){
    int m,s,c;
	cin>>m>>s>>c;
	for(int i=1;i<=c;++i){
		cin>>a[i];
	} 
//	对牛棚编号排序 
	sort(a+1,a+c+1);
//	计算最近牛棚之间的距离 
	for(int i=2;i<=c;++i){
		d[i-1]=a[i]-a[i-1]-1;
	}
//	对距离排序,方便之后选择合并 
	sort(d+1,d+c);
//	为每个牛棚添加一块板子 
	int ans=c;
	if(m<c){
//		牛棚数>板子数 
		for(int i=1;i<=c-m;++i){
//			选取最短的距离加入 
			ans+=d[i];
		}
	}
	cout<<ans<<endl;
	
	
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值