【挑战程序设计竞赛读书笔记】贪心算法

贪心算法:

  贪心法就是遵循某种规则(这个规则可以有很多种,但是得出来的贪心算法不一定是对的),不断贪心地选取当前最优策略的算法设计方法。而DFS,BFS,动态规划是从某个状态不断的找,而贪心是按照一定规律去找。
例题1:

在这里插入图片描述

分析:

  很典型的贪心算法问题,读完题后的第一感觉就是,若要让硬币用的最少,那么优先把面值大的硬币用完。

代码:
#include<bits/stdc++.h> 
using namespace std;
const int v[6]={1,5,10,50,100,500};//硬币面值
int C[6];//硬币数量
int A;//支付的钱 
int use[6]={0,0,0,0,0,0};//初始使用0张
int counts=0;//总共使用次数 
void solve()
{
	for(int i=5;i>=0;i--)
	{
		int n=A/v[i]<C[i]? A/v[i]:C[i];//用的硬币数量 不能超出最大值 
		use[i]=n; //对应面值使用数量 
		A= A-n*v[i];//还剩多少钱没支付 
		counts+=n; //使用总数 
	}
	cout<<counts<<"(";
	for(int i=5;i>=0;i--)
	{
		if(use[i])
		{
			printf("%d元硬币%d枚,",v[i],use[i]); 
		}
	}
	 printf("合计%d枚)",counts);
 } 
 int main()
 {
 	for(int i=0;i<6;i++)
 	{
 		cin>>C[i];
	 }
	 cin>>A;
	 solve();
 }
例题2:

在这里插入图片描述

分析:

  也是个贪心问题,但是贪心的方法感觉有好几种比如1.优先选开始时间早的2.优先选结束时间最早的 3 优先选择用时最短的 2是正确的(脑补一下好像确实是,但是目前还没学会该怎么证明QAQ)

代码:
#include<bits/stdc++.h> 
#define MAX 10000
using namespace std;
int n;//工作数量
int s[MAX],t[MAX];//开始与结束 的时间
pair<int,int> itv[MAX];
void solve()
{
	for(int i=0;i<n;i++)
	{
		itv[i].first=t[i];//因为是优先取结束时间最早的 所以把结束时间存为first 方便用sort排序 
		itv[i].second=s[i];
	}
	sort(itv,itv+n);//排序
	int res=0,t=0;//t记录的是当前可以工作的最早时间
	for(int i=0;i<n;i++)
	{
		if(itv[i].second>t)
		{
			res++;
			t=itv[i].first;//将当前最早时间替换成选择工作的结束时间 
		} 
	 } 
	printf("%d\n",res);
}
 
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>s[i];
		cin>>t[i];
	}
	solve();
}
例题3:在这里插入图片描述
分析:

  按照贪心的思想每次都选最小的,如果相同的话就往里面选小的。。依次类推,所以可以通过比较正向的字典序和反向的字典序来选。

代码:
#include<bits/stdc++.h>
#define MAX 999
typedef long long ll;
using namespace std;
int N;
char s[MAX];
void solve()
{
	int a=0;
	int b=N-1;
	while(a<=b)
	{
		bool left=false;//左小还是右小
		for(int i=0;a+i<=b;i++)
		{
			if(s[a+i]<s[b-i])
			{
				left=true;
				break;
			}
			else if(s[a+i]>s[b-i])
			{
				left=false;
				break;
			}
		 } 
		if(left) putchar(s[a++]);
		else putchar(s[b--]);
	} 
 } 
int main()
{
	cin>>N;
	cin>>s;
	solve();
 } 
例题4

在这里插入图片描述p

分析:

  贪心规则:用一个半径R的距离尽可能的包括更多点。从最左边的点(未被标记)开始,找到R范围内能包括的最远的点,这个点就是标记点,然后从这个点开始,从左找到R范围内能包括的最远的点,至此,一个范围已经找出来了。然后再从刚才那个范围最右边的点的下一个开始反复 直到到达最后一个点。

代码:
#include<bits/stdc++.h>
#define MAX 999
typedef long long ll;
using namespace std;
int N;
int R;
int X[MAX];

void solve()
{
	sort(X,X+N);
	int p=0;
	int counts=0;
	while(p<N)
	{
		int biao;
		for(biao=p;biao<N;biao++)
		{
			if(X[p]+R<X[biao])
				break;
		}
		biao=biao-1;
		counts++;
		for(p=biao;p<N;p++)
		{
			if(X[biao]+R<X[p])
				break; 
		}
	}
	cout<<counts;
}
int main()
{
	cin>>N;
	cin>>R;
	for(int i=0;i<N;i++)
		cin>>X[i];
	solve();

}
例题5

在这里插入图片描述

分析:

  可以类比哈夫曼树,相当于最后切割出来的每块板子,在之前每一轮切割中都会被计算一次,所以每块板子提供的开销为板子长度× 切割次数,所以问题可以归结为,求每块板子长度(叶子结点)×切割次数(深度)的和的最小值。

代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
int N,L[99999];
void solve()
{
	ll ans=0;
	while(N>1)//直到只剩下一个木板(初始)
	{
		int fmin=0,smin=1;//默认第一小和第二小是第一个板和第二个板
		if(L[fmin]>L[smin])
		{
			int temp=fmin;
			fmin=smin;
			smin=temp;
		}//如果不是就交换
		for(int i=2;i<N;i++) 
		{
			if(L[i]<fmin)
			{
				smin=fmin;
				fmin=i;//如果比最小的小 
			}
			else if(L[i]<smin)
			{
				smin=i;//如果比次小的小 
			}
		}
		int t =L[fmin]+L[smin];
		ans +=t;
		if(fmin==N-1)
		{
			int temp=fmin;
			fmin=smin;
			smin=temp;
		}
		L[fmin]=t;
		L[smin]=L[N-1];
		N--;
	 } 
	 printf("%lld\n",ans);
}
int main()
{
	cin>>N;
	for(int i=0;i<N;i++)
	{
		cin>>L[i];
	}
	solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值