sdau acm 总结2 贪心算法

贪心算法

一种求最优解的方法,它是按照某种最优策略,把复杂的问题层层分解成子问题(每次一般只有一个),并由子问题的最优解“回溯”出整个问题的最优解。

首先应注意在解题时首先要看题目是否符合贪心策略,(如果一个问题可以同时用几种方法解决,就可以用贪心算法)
再看如何选择贪心标准来求取最优解。

贪心算法大意为从一个初始的出发点开始,逐渐逼近题目给定的目的,快速求解得更好的答案。

贪心算法好像不能用来求最大最小解类似的问题;

适用范围感觉较小,但方法简便;

举例说明;

经典背包类型问题

给定一个载重量为M的背包,考虑n个物品,其中第i个物品的重量 ,价值wi (1≤i≤n),要求把物品装满背包,且使背包内的物品价值最大。

解题思路

当我看到这个题,让背包内的物品价值最大,首先可以想到物品的性价比,按照物品的性价比来选择先装哪一种,装多少,通过把物品的性价比排序,再按照背包的承载量装取多少物品,直到背包装满,这样一来,背包的物品价值最大。(这个基本的贪心想法还是很好理解的)

前提:背包的称重量为 50

假设有三个物品分别为: 1,2,3;

它们 的 重量 分别 为 :10,20,30;

价值分别为:60,100,120

可以得到性价比分别为:6,5,4

首先,通过贪心算法,1号物品的性价比最高,优先选择1号,这样背包的称重量还剩40

然后,看性价比其次的2号,装完后背包的称重量还剩20

最后,装性价比最低的3号,把3号拆分重量为20;这样背包刚好装满且价值最大(贪心中物品可拆)。
建立数据结构:
背包类型问题

 struct bag{
	
	int w;			//物品的重量
	int v;			//物品的价值
	double c;		//性价比
}a[1001];           存放物品的数组

bool cmp(bag a, bag b)
{
	return a.c >= b.c;       //按性价比降序
}
sort(a, a+n, cmp);       //用标准模板库函数排序(使用stable_sort()函数,在性价比相同时保持输入的顺序)

double knapsack(int n, bag a[], double c)
{
  double cleft = c;        //背包的剩余容量
  int i = 0;
  double b = 0;          //获得的价值
                   //当背包还能完全装入物品i
  while(i<n && a[i].w<cleft)
  {
    cleft -= a[i].w;
    b += a[i].v;
    i++;
  }
  //装满背包的剩余空间
  
  if (i<n) b += 1.0*a[i].v*cleft/a[i].w;
  return b;
}

最少张钱的类似问题,
比如:找零钱问题,或交钱问题;

例:
对于人民币的面值有1元 ,5元, 10元, 20元, 50元 ,100元

输入找零的钱,输出找钱方案中最少张数的方案,
比如123元,最少是1张100的,1张20的,3张1元的,一共5张

我的解题思路:
如果要使钱的张数最少,面值大的纸币性价比高,每一步尽可能用面值大的纸币,面值大的纸币占用零钱总体相对较多,最后再选择面值小的纸币充当零头。
这样就可以保证钱的张数最少。(感觉这个跟背包问题类似)

首先我们在程序中已经事先将钱的价值按照从小到大的顺序排好。

const int N = 7;
int Count[N] = {3, 2, 1, 2, 2, 3};      //   纸币对应面值的数量

int value[N] = {1, 5, 10, 20, 50, 100};      // 对纸币价值排序
int ans=0;

void solve(int money){
    for(int i = N-1; i >= 0; i--)
    {
        int c = min(money / value[i], Count[i]);
        money = money - c * value[i];
        if(c != 0)
        {
            ans+=c;
        }
    }
    if(money > 0){
        printf("\n不能找零");
    }else{
        printf(%d\n“,ans);
    }
}

字典序最小问题

给定长度为N的字符串S,要构造一个长度为N字符串T。T是一个空串,反复执行下列任意操作:
从S的头部删除一个字符,加到T的尾部;
从S的尾部删除一个字符,加到T的尾部;
目标是要构造字典序尽可能小的字符串T。

字典序(指从前到后比较两个字符串的大小的方法。首先比较第1个字符,如果不同则第1个字符较小的字符串更小,如果相同则继续比较第2个字符…反复继续,来比较整个字符串的大小。)

从字符串性质上看,无论T的末尾多大,只要前面部分的较小即可。
把‘不断取S得开头和末尾中较小的一个字符放到T的末尾’作为贪心法所遵循的策略。

思路:
按照字典序比较S和将S反转后的字符串S’。

如果S较小,从S的开头取出一个字,追加到T的末尾。

如果S较小,从S的末尾取出一个字,追加到T的末尾。(如果相同,则取谁都可以

int main()
{
	int n=6;
	char S[7]="ACDBCB";	
	int a=0,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--]);
	}		
	return 0;

分发饼干问题
你想要给你的孩子们一些小饼干。
但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;
并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。
你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
注意:你可以假设胃口值为正。一个小朋友最多只能拥有一块饼干

原则:大尺寸的饼干分给胃口大的孩子。

 int findContentChildren(vector<int>& g, vector<int>& s) 
 {                                                    //胃口是g,饼干是s
        if(g.empty()|| s.empty()) 
        return 0;        
       
        sort(g.begin(),g.end());           //排序胃口大小
        sort(s.begin(),s.end());          //排序饼干尺寸大小
        
        int res=0, index=s.size()-1;
        for(int i=g.size()-1;i>=0;)
        {
            if(index>=0)
            {
                if(g[i]<=s[index])
                {
                    res++;
                    index--;     
                }
                i--; }
            else{
                break;
            }
        }
        return res;
    }

学习的小结:
因为用贪心算法只能通过解局部最优解的策略来达到全局最优解,因此,一定要注意判断问题是否适合采用贪心算法策略,找到的解是否一定是问题的最优解。

刚开始通过老师讲贪心算法的时候,还是很懵逼的,不明白是个什么概念,本来以为和递归差不多像是一种解题模式,之后通过接触题目才明白,贪心算法是一种想法,帮助你解题的解出最优解的一种解题思路和方法。

不管现在自己有多菜,经过努力,我想我一定能够学懂和掌握这门课,学到知识提升自己。
世上无难事,只要肯登攀。;






所 谓 贪 心,即 尽 所 能 的 做 到 最 优 做 到 最 好 。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值