ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

暑期集训(3) 贪心

2021-07-08 21:34:06  阅读:161  来源: 互联网

标签:int sum cin 暑期 long 最优 集训 贪心


贪心

一.概念

1)官方解释

贪心法是一种解决最优问题的策略。它是从问题的初始解出发,按照当前最佳的选择,把问题归纳为更小的相似的子问题,并使子问题最优,再由子问题来推导出全局最优解

使用贪心方法需要注意局部最优与全局最优的关系,选择当前状态的局部最优并不一定能推导出问题的全局最优。

实际生活中,经常需要求一些问题的“可行解”和“最优解”,这就是所谓的“最优化”问题。
一般来说,每个最优化问题都包含一组“限制条件”和一个“目标函数”,符合限制条件的问题求解方案称为可行解,使目标函数取得最佳值(最大或最小)的可行解称为最优解。

求解最优化问题的算法很多,例如穷举、搜索、动态规划等。贪心法也是求解这类问题的一种常用方法。

2)个人理解

贪心是一种解题方法与步骤,在初级题目里,贪心思想是拿分的好手
见到一道疑似贪心的题,尝试用自己的方式证明,通过灵活运用给出的样例,推演规则,同时在可能的情况下添加更多特殊解法与判别
同一道题可能有不同的贪心策略
就例如双人过桥这道题,在无法确定是在快的一个人往返还是两个人往返时,只要时间复杂度允许,完全可以将两种方法比较去最优解,事实证明2o(n)的复杂度是完全够用的
在接触到一些高级的题目时
我们也可以运用贪心来剪枝
所谓贪心只能过样例
在想拿更高的分的情况下又怕超时
例如使用递归时
我们就可以通过贪心直接舍弃一些 不可能的点
加快运行速度
一石二鸟

3)步骤

a、确定贪心策略

b、根据贪心策略,一步一步得到局部最优解

c、将局部最优解合并起来就得到全局最优解

4)特点

1.贪心选择性质:所求问题的整体最优解可以通过一系列局部最优的选择来达到,这样的选择称为贪心选择。这些选择只依赖于以往所做过的选择,决不依赖于将来的选择

2.最优子结构性质:当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。

二.例题及延伸证明&优化方法

1.国王的游戏

在这里插入图片描述

1)思路

在见到这道题的时候,明显直接循环是会超时的
并且int会超范围,必须用高精度方可
但最最最最重要的不是这些细枝末节
而是贪心方法的证明
到底哪个为重?
借用老师的一句话:看哪个部分对答案影响最大
但我还是没有证明的方法
于是我找到了题解。。。
看了眼数论证明
搞懂了
结论是:al * ar<bl * br

2)60分代码

因为

本蒟蒻不太会用高精度QWQ

所以只拿了
60分
不过大体方案是对的

#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn=21474836;
int x,y;
struct HW
{
    long long   l;
    long long   r;
    long long   qzh;
    long long   price;
    long long   prepare;
}a[maxn];
bool mycmp(HW p,HW q)
{
    return p.prepare<q.prepare;
}
void init()
{
    cin>>n;
    cin>>x>>y;
   
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].l>>a[i].r;
        a[i].prepare=a[i].l*a[i].r;
    }
}

int main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
    /*前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。*/
    init();
    sort(a+1,a+n+1,mycmp);
//    for(int i=1;i<=n;i++)
//    cout<<a[i].l<<' '<<a[i].r<<endl;

    a[0].qzh=x;
    for(int i=1;i<=n;i++)
    {
        a[i].qzh=a[i-1].qzh*a[i].l;
        a[i].price=a[i-1].qzh/a[i].r;
    }
//     for(int i=1;i<=n;i++)
//    cout<<a[i].qzh<<endl;
    
    int maxx=-1;
    for(int i=1;i<=n;i++)
    {
    	if(a[i].price>maxx) maxx=a[i].price;
    }
    cout<<maxx;
    return 0;
}

注释就不详细加了
但大体的方案是:先整体输入记录左右手乘积的值,并借此排序
为了节省时间乘积用了== 前缀和 ==不懂得可以看我前天的博客 前缀和应用

3)应用方案—对数证明

如下

证明详细过程

2.最大的子序列和

在这里插入图片描述

1)思路

用一个sum记录当前前缀和,一路累积过去,如果前缀和sum变成了负数,那么下一个数就不需要前面的数了(因为还不如只选它一个),这时把sum置为0,再继续累加

2)代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=10000100;
int n,a[maxn];
int maxx=-maxn,sum=0;
int main()
{
	freopen("maxsum.in","r",stdin);
	freopen("maxsum.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)   cin>>a[i];
	for (int i=1;i<=n;i++)
	{
	   sum+=a[i+1];
	   if (sum>maxx)  maxx=sum;
	   if (sum<0)  	  sum=0;
	}
	
	if(maxx==0) cout<<a[n];
	
	else cout<<maxx;
	return 0;
}

3)延伸方法–尺取法

尺取法

4)延伸例题–最短自序列

<1> 思路

根据尺取法的特性,判断左右区间下标求解

<2> 代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=10010;
bool flag=true;
int main()
{
    freopen(",in","r",stdin);
    freopen(",out","w",stdout);
	int n;
	cin>>n;

	for(int i=n;i>=1;i--)
	{
		int N,S,a[maxn];

        int ans=N+1,s=1,t=1,sum=0;
		cin>>N>>S;

		for(int j=1;j<=N;j++)
        {
            cin>>a[j];
        } 
		
		while(flag)
		{
			while(t<=N&&sum<S)
			{
				sum+=a[t++];
            }

			if(sum<S) 
            {
                break;
            }

			ans=min(ans,t-s);
			sum-=a[s++];
		}
		if(ans>N)
        {ans=0;}

		cout<<ans<<endl; 
	}
}

3.最大整数

在这里插入图片描述

1).思路

很显然,一见面就知道是到贪心题目
而策略呢?
第一眼见到发现其优先比较方法与字符串相差不多
也就是所谓的字典排序
先排列首位
而之后的则需要改变一下
举一个反例
2 个数:9,91
显然字符串直接比较会把91放在前面
而在总数位不变的情况下
靠前的数位越大越好
所以我们要自己增加sort函数的判断
我的想法是全补上0判断
但代码较为繁琐
如下

2).代码

#include<bits/stdc++.h>
using namespace std;
int n;
string s[25];
bool pd(string a,string b)
{
    int hy,hx;
    hy=a.size(),hx=b.size();
    int t=max(hy,hx);
    
    if(t==hy)
    {
        for(int i=hx+1;i<t;i++)
        {
            b[i]=0;
        }
    }
    else
    {
        for(int i=hy+1;i<t;i++)
        {
            a[i]=0;
        }
    }

    return a>b;
}

bool cmp(string a,string b)
{
    if(a[0]!=b[0]) return a>b;
    else return a+b>b+a;
}

int main()
{
     freopen("max.in","r",stdin);
     freopen("max.out","w",stdout);

    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>s[i];
    }
    
    sort(s,s+n,/*pd*/cmp);

    for(int i=0;i<n;i++)
    cout<<s[i];
    return 0;
}

总结

贪心不仅要“贪”,更要用“心”
在拿到题时不能盲目求解
想好策略与方法
在代码运行时间足够的前提下其实更因该谨慎使用
自己动手推出来的步骤才是最保险的!!

标签:int,sum,cin,暑期,long,最优,集训,贪心
来源: https://blog.csdn.net/m0_55828624/article/details/118581464

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有