贪心算法例题

2021-3-27上课总结

一、删数问题

题目描述
输入一个高精度的正整数n(≤240位),去掉其中任意s个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的n和s,寻找一种方案,使得剩下的数字组成的新数最小。 (仔细思考,这道题并不是很水)

输入格式
两行,每行一个整数: n(n为小于240位的整数) s

输出格式
最后剩下的最小数。

分析:
1、相信许多人第一次看见这道题都觉得它很简单,不是只要sort排序一下,再删除最后s位数不就好了嘛?其实远不止如此;
2、如果按照第一条分析的来写,那么就会成功的报零,不要问我咋知道。这样做的漏洞是改变了数位次序,不符合题意,所以就会错;
3、首先看题目,输入的是高精度数,所以用字符串还是很香的,然后需要连续寻找这样一对数:前一个数比后一个数大,然后删除前一个数。(注意:因为有可能从头到尾循环完了以后还有这样的数,所以要循环s次

具体过程:
1、输入字符串n和s,因为在运算过程中n.size()这个长度函数无法直接运算,所以我们要开个备份len来运算;

	string n;
	int s;
	cin>>n;
	cin>>s;
	int len=n.size();

2、开始循环:第一重,从1到s,循环次数;第二重:从前往后枚举,只要找到一对前比后大的数就删除前数,然后break(因为要删掉s个数,所以break可以有效控制次数);

	for(int i=1;i<=s;++i)
	{
		for(int j=0;j<len-1;j++)
		{
			if(n[j]>n[j+1])
			{
				for(int k=j;k<len-1;++k)
				n[k]=n[k+1];//删数代码
				break;
			}
		}
		len--;
	}

3、因为可能有前导零,所以输出要长个心眼。

	bool flag=0;
	for(int i=0;i<len;i++)
	{
		if(n[i]!='0') flag=1;
		if(flag) cout<<n[i];
 	}

二、三值排序

题目描述:
排序是一种很频繁的计算任务。现在考虑最多只有三值的排序问题。一个实际的例子是,当我们给某项竞赛的优胜者按金银铜牌排序的时候。在这个任务中可能的值只有三种1,2和3。我们用交换的方法把他排成升序的。 写一个程序计算出,给定的一个1,2,3组成的数字序列,排成升序所需的最少交换次数。

输入格式
第 1 行: 奖牌个数N (1 <= N <= 1000) 第 2 行到第 N+1 行: 每行一个数字,表示奖牌。共N行。(1…3)

输出格式
共一行,一个数字。表示排成升序所需的最少交换次数。

分析:
1、这道题看似很简单,但实际上需要一点策略;
2、这道题不能用sort,这样会导致后面的运算超时;
3、要记录123的个数,方便统计。

具体步骤:
1、输入,统计个数;

	int n,s1=0,s2=0,s3=0,s=0,ss=0,sss=0,ppp;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
		if(a[i]==1) s1++;
		if(a[i]==2) s2++;
		if(a[i]==3) s3++;
	}

2、策略: 先从s1开始循环,一直到n-s3(s3是3的个数,n-s3相信大家能理解),如果里面有三,s++(说明在这一段里有s个3,要换s次),再从n-s3开始循环,到n,如果里面有二,ss++(说明在这一段里有ss个2,要换ss次);

	for(int i=s1;i<n-s3;++i)
	{
		if(a[i]==3)
		s++;
	}
	for(int i=n-s3;i<n;++i)
	{
		if(a[i]==2)
		ss++;
	}

3、从0到s1,如果他不是一,sss++;

	for(int i=0;i<s1;++i)
	{
		if(a[i]!=1)
		sss++;
	}

4、策略: 输出sss加s与ss的最大值。理由: s与ss的最大值说明至少要循环这么多次才能让3或2排好序,加上sss说明至少要这么多次才能让1与 2或3排好序,这样才能保证三个都排好序。

	cout<<sss+max(s,ss);
posted @ 2021-04-03 10:05  best_brain  阅读(27)  评论(0)    收藏  举报  来源