贪心算法总结

贪心算法是一种求最优解的方法,不是从整体上思考问题,而是选择局部最优解,由局部进而得到整个问题的最优解。
利用贪心策略解题,需要注意两个问题:
(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;  
    }  
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值