目录
贪心算法
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;
}