贪心算法总结

贪心算法是一种求最优解的方法,不是从整体上思考问题,而是选择局部最优解,由局部进而得到整个问题的最优解。
利用贪心策略解题,需要注意两个问题:
(1)该题是否适合于用贪心策略求解;
(2)如何选择贪心策略,以得到问题的最优/较优解。
所以如何去选择贪心策略是很重要的。

例题
1.最优装载问题

有一批集装箱要装上一艘载重量为c的轮船,其中集装箱i的重量为wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。

贪心策略:优先装载最轻的

算法实现

struct load {
	int index;		//集装箱编号
	int w;			//集装箱重量
}box[1001];
bool cmp (load a, load b)
{
	return a.w<b.w ;//升序排列
}
while (cin>>c>>n)
{
  memset(box, 0, sizeof(box));
  memset(x, 0, sizeof(x));//清空
  for (int i=1; i<=n; i++) 
  {
    cin>>box[i].w;
    box[i].index = i;
  }
  //按集装箱的重量升序排序
  sort(box, box+n+1, cmp);
  if (box[1].w>c) 
  {
    printf("No answer!\n");
    continue;
	}
 int i;
  for (i=1; i<=n && box[i].w<=c; i++)
  {
    x[box[i].index] = 1;
    c -= box[i].w;
  }
  cout<<i-1<<endl;//输出装载的集装箱数量
  for (i=1; i<=n; i++)
    if (x[i]) cout<<i;
  cout<<endl;//输出装载的集装箱编号

2.背包问题

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

贪心策略:选择性价比最高的

算法实现

struct bag{
	int w;			//物品的重量
	int v;			//物品的价值
	double c;		//性价比
}a[1001];			//存放物品的数组
bool cmp(bag a, bag b)
{
	return a.c >= b.c;降序
}
//形参n是物品的数量,c是背包的容量,数组a是按物品的性价比降序排序
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;
}

3.货币找零

假设1元、2元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5, c6张。现在要用这些钱来支付K元,至少要用多少张纸币?

贪心策略:每一步尽可能用面值大的纸币

算法实现

int Count[7] = {3, 0, 2, 1, 2, 2, 3};
int value[7] = {1, 2, 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)
    {
       cout<<"不能找零"<<endl;
    }
    else
        cout<<ans<<endl;
}

4.区间调度问题

有n项工作,每项工作分别在Si开始,Ti结束。例如S={1,2,4,6,8},T={3,5,7,8,10}。对每项工作,你都可以选择与否,若选择参加,则必须至始至终参加全程参与,且参与工作的时间段不能有重叠,你最多能选择几项工作。

贪心策略:每次都选取结束时间最早的

算法实现

int S[5]={1,2,4,6,8};
int T[5]={3,5,7,9,10}; 
pair<int, int> itv[n];//对工作排序的pair数组
int solve()
{
	//为了让结束时间早的工作排在前面,把T存入first,把S存入second
	for(int i = 0; i < n; i ++) 
	{
 
		itv[i].first = S[i];
		itv[i].second = T[i];
	}
	sort(itv, itv + n);
	int count = 0;//选取的结果
	int t = 0;	//最后所选工作的结束时间
	for(int i = 0; i < n; i ++) 
	{
		if(t < itv[i].first) 
		{
			count ++;
			t = itv[i].second;
		}
	}
	return count;
}

5.字典序最小问题

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

贪心策略:不断取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--]);
	}		
}

6.分发饼干

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

贪心策略:大饼干分给胃口大的孩子

算法实现:

int fn(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;
 }

7.小船过河问题

只有一艘船,能乘2人,船的运行速度为2人中较慢一人的速度,过去后还需一个人把船划回来,问把n个人运到对岸,最少需要多久。

贪心策略:
先将所有人过河所需的时间按照升序排序,我们考虑把单独过河所需要时间最多的两个旅行者送到对岸去,有两种方式:
最快的和次快的过河,然后最快的将船划回来;次慢的和最慢的过河,然后次快的将船划回来,所需时间为:t[0]+2t[1]+t[n-1];
最快的和最慢的过河,然后最快的将船划回来,最快的和次慢的过河,然后最快的将船划回来,所需时间为:2
t[0]+t[n-2]+t[n-1]。

算法实现;

int main()  
{  
    int a[1000],t,n,sum;  
    cin>>t; 
    while(t--)  
    {  
        cin>>n;  
        sum=0;  
        for(int i=0;i<n;i++) 
        cin>>a[i];  
        sort(a,a+n);
        while(n>3)  
        {  
            sum=min(sum+a[1]+a[0]+a[n-1]+a[1],sum+a[n-1]+a[0]+a[n-2]+a[0]);  
            n-=2;  
        }  
        if(n==3) 
        	sum+=a[0]+a[1]+a[2];  
        else if(n==2) 
        	sum+=a[1];  
        else 
        	sum+=a[0];  
        cout<<sum<<endl;  
    }  
}  

### 贪心算法的核心思想 贪心算法是一种通过逐步做出局部最优的选择来解决问题的方法,其目标是在每一步都选择当前情况下最好的选项,从而期望最终得到全局最优解[^2]。然而需要注意的是,这种策略并不总是能够保证获得真正的全局最优解,因此在应用时需谨慎验证。 #### 局部最优与整体最优的关系 贪心算法的关键在于如何定义“局部最优”。例如,在找零钱问题中,优先使用面额较大的钞票可以减少所需钞票的数量,这是一种直观的局部最优策略[^2]。但是并非所有问题都能如此简单地解决;某些复杂场景下可能需要更深入的理解才能正确设计出有效的贪心规则。 #### 学习过程中的挑战与成长 对于初学者而言,掌握贪心算法可能会遇到一定困难。正如一位学习者所描述,“刚开始都不怎么会做”,这表明初次接触此类方法时常感到困惑甚至无从下手[^1]。面对这些问题,可以通过观看在线教程、查阅参考资料以及与其他同学交流等方式逐渐积累经验并形成自己的思考模式。“慢慢地才对贪心算法有了一些规律上的总结”,说明持续的努力加上实践是非常重要的环节之一[^1]。 以下是基于C++实现的一个典型例子——跳跃游戏(Jump Game),它展示了如何利用贪心思路高效求解特定类型的问题: ```cpp #include <iostream> using namespace std; int canJump(int* nums, int size){ int lastPos = size - 1; for (int i = size - 1; i >= 0; --i){ if (i + nums[i] >= lastPos){ lastPos = i; } } return lastPos == 0 ? 1 : 0; } int main(){ int a[] = {2,3,1,1,4}; cout << canJump(a,sizeof(a)/sizeof(*a))<< endl; } ``` 此程序片段实现了这样一个功能:给定一系列非负整数表示每个位置的最大跳远距离,请判断是否可以从第一个位置出发到达最后一个位置。这里采用逆向思维的方式,不断更新最远可达到的位置直到起点为止[^3]。 ### 结论 综上所述,虽然贪心算法看似简单易懂,但在实际运用过程中仍需仔细考量各种因素以确保解决方案的有效性和准确性。随着不断的练习和探索,人们不仅可以提升对该技术的认识水平,而且还能培养良好的编程习惯和技术素养。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值