贪心 二分查找和二分答案

贪心

知识点

局部最优解->整体最优解

贪心算法理论基础!_哔哩哔哩_bilibili

选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

证明贪心策略的有效性

· 反证法

· 数学归纳法

image.png

例题 

P1090 [NOIP2004 提高组] 合并

P3817 小A的糖果

P3817 小A的糖果(贪心算法)_矩阵科学的博客-CSDN博客

image.png

#include <cstdio>//为了快
long long n,a[100010],x,ans,yy;//我知道可以不定义数组,但为了用空间换时间(怕超时)
int main()
{
    register int i;//register定义循环变量据说能加速
    scanf("%lld%lld",&n,&x);//注意是%lld
    for (i=1;i<=n;i++)//从1~n很好地保证了第一个是a[0]和a[1],相当于单独考虑a[1]
    {
        scanf("%lld",&a[i]);//输入
        if (a[i]+a[i-1]>x)yy=a[i]+a[i-1]-x,a[i]-=yy,ans+=yy;//两数之和超过,优先吃后面的,贪心(为了使后面加起来更小)
    }
    printf("%lld",ans);//lld哦
    return 0;//记得返回0
}

 

[NOIP2018 提高组] 铺设道路

#include<bits/stdc++.h>
using namespace std;
int n,a[100005];
long long ans=0;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)     cin>>a[i];
	for(int i=2;i<=n;i++)     if(a[i]>a[i-1]) ans+=a[i]-a[i-1];
	cout<<ans+a[1];
	return 0;
}

376.摆动序列

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
public:
 int wiggleMaxLength(vector<int>& nums) {
 if (nums.size() <= 1) return nums.size();
 int curDiff = 0; // 当前⼀对差值
 int preDiff = 0; // 前⼀对差值
 int result = 1; // 记录峰值个数,序列默认序列最右边有⼀个峰值
 for (int i = 0; i < nums.size() - 1; i++) {
 curDiff = nums[i + 1] - nums[i];
 // 出现峰值
 if ((curDiff > 0 && preDiff <= 0) || (preDiff >= 0 &&
curDiff < 0)) {
 result++;
 preDiff = curDiff;
 }
 }
 return result;
 }};

P1090 [NOIP2004 提高组] 合并果子

[NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G - 洛谷

补充知识:优先队列

定义:

priority_queue<int>q;

从小到大:

priority_queue<int,vector<int>,greater<int> >q;

如果你想从大到小的话可以重载运算符:

struct Node{
	int x,y;
	Node(int a=0, int b=0):
		x(a), y(b) {}
};
 
struct cmp{
	bool operator()(Node a, Node b){
		if(a.x == b.x)	return a.y>b.y;
		return a.x>b.x;
	}
};

priority_queue<Node,vector<Node>,cmp>q;

或者你也可以用less

priority_queue<int,vector<int>,less<int> >q;

题解:

#include<bits/stdc++.h>
using namespace std;
int n,x,ans;
priority_queue<int,vector<int>,greater<int> >q;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>x,q.push(x);
	while(q.size()>=2){//
		int a=q.top(); q.pop();
		int b=q.top(); q.pop();
		ans+=a+b;
		q.push(a+b);
	}
	cout<<ans<<endl;
	return 0;
}

牛牛学括号

链接:https://ac.nowcoder.com/acm/problem/21579
来源:牛客网
(乘法原理)

题目描述

牛牛最近在学习括号匹配问题


给你一个合法的括号序列,每次操作分两步,第一步删除第一个左括号,第二步删除某一个右括号,要保证删除之后的括号序列还是合法的,求将括号删到空为止一共有多少种不同的删除方法,两种方法不同当且仅当存在某一步右括号的删除位置不同,答案膜1e9+7

 

输入一个字符串s只包含左右括号, 2 ≤ |s| ≤ 2500输出一个整数

输入描述:

输出描述:

示例1

输入

()()()()()

输出

1

示例2

输入

(((())))

输出

24

示例3

输入

((()))(()(()))((()))

输出

432

备注:

子任务一30分:n<=20

子任务二30分:n<=100

子任务三40分:n<=2500

标签:贪心 乘法法则

//加法原理 乘法原理
#include <cstdio>
#include <iostream>
#include <stack>
#include <string>
#include <cstring>
using namespace std;
const int mod = 1e9+7;
int main ()
{
    char s[2505];
    int sum=1;
    cin >> s;
    stack<char>a;
    a.push(s[0]);
    for (int i=1;i<strlen(s);i++)
    {
        if(s[i]=='(')
        {
            a.push(s[i]);
        }
        else
        {
            sum=(sum * a.size()) % mod;
            a.pop();
        }
    }
    cout << sum << endl;
    return 0;
}


第二种方法:
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
typedef long long ll;
ll mod = 1000000007;
int main()
{
    string s;
    cin >> s;
    int len = s.length();
    ll sum=1;
    ll ans = 1;
    for (int i=1;i<len;i++)
    {
        if(s[i]=='(') sum++;
        else 
        {
            ans = ans * sum % mod;
            sum--;
        }
    }
    cout << ans << endl;
    return 0;
}

牛牛的旅游

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

题目描述

有n个人在x轴上,每个人的坐标是一个整数,牛牛是一个旅行商人,他在轴上穿梭并与这n个人交易,交易的商品只有一种,每一个人都有对这种商品的需求或者供应,如果delta[i] 是正数,表示这个人要供应delta[i]的数量,如果是负数,表示这个人需要−delta[i]的购买量,保证delta的和非负,一开始牛牛在0位置,手里没有任何商品,每一秒他可以往左或者往右走1个单位的距离,如果他和一个人在同一个位置,就可以与之交易,交易是瞬间发生的,每笔交易的数量是由牛牛决定的,当然他不能卖出超出自己手里含有的商品数量,行走过程中牛牛手里可以拿着任意多的商品,到了一个人的位置,也可以选择不与之交易,最终牛牛需要满足以下两点

1:牛牛必须满足所有人的需求

2:旅行必须要在最后一个人的位置结束。

求最少需要花费多少时间。
 

输入描述:

第一行输入一个整数n (1 ≤ n ≤ 300)

第二行输入n个元素pos[i]( 1 ≤ pos[i] ≤ 105), 表示每个人的位置

第三行输入n个元素delta[i](-105 ≤ delta[i] ≤ 105)表示每个人的需求


保证pos单调递增,delta的和非负

输出描述:

输出一个整数表示最少需要花费的时间

示例1

输入

5 3 14 15 92 101 -3 2 3 -3 1

输出

143

示例2

输入

8 1 2 4 8 16 32 64 128 -1 -1 -1 -1 1 1 1 1

输出

382

备注:

总共5个人,0号人在3位置,需要3的购买量. 1号人在14位置有2的供应量. 2号人在15位置有3的供应量. 3号人在92位置有3的购买量。4号人在101位置有1的供应量,最优路线如下
第一步走到15位置. 买下2号人所有的供应量
第二步走到14位置,买下1号人所有的供应量,当前alice一共有5个单位的量。
第三步走到3位置,卖出3个单位。还剩2个单位
第四步走到101,买下4号人的供应量,当前手里一共有3个单位
第五步走到92,卖给3号人3个单位
最后走到101结束旅程
一共花的时间是 15+1+11+98+9+9 = 143.
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,l,now;
int pos[301];
int delta[301];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>pos[i];
    }
    for(int i=1;i<=n;i++){
        cin>>delta[i];
    }
    m=0;
    l=0;
    now=0;
    for(int i=1;i<=n;i++){
        if(now<0&&now+delta[i]>=0){
            m+=(pos[i]-l)*2;
        }
        if(now>=0&&now+delta[i]<0){
            l=pos[i];
        }
        m+=pos[i]-pos[i-1];
        now+=delta[i];
    }
    cout<<m<<endl;
    return 0;
} 
#include<bits/stdc++.h>
using namespace std;
int a[305],b[305],n,l,sum,ans;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)cin>>b[i];
    for(int i=1;i<=n;i++){
        if(sum>=0&&sum+b[i]<0)l=a[i];//供应不足
        if(sum<0&&sum+b[i]>=0)ans+=(a[i]-l)<<1;//能供应回来
        ans+=a[i]-a[i-1];//接着往前走
        sum+=b[i];
    }
    cout<<ans<<endl;
    return 0;
}

牛牛的战役

链接:登录—专业IT笔试面试备考平台_牛客网

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
struct difang
{
    int b;
    long long c;
}d[55];
bool cmp (difang x,difang y)
{
    return x.b<y.b;
}
int main ()
{
    int a[55];
    int n,m;
    cin >> n;
    for (int i=1;i<=n;i++)cin >> a[i];
    cin >> m;
    for (int i=1;i<=m;i++)cin >> d[i].b;
    for (int i=1;i<=m;i++)cin >> d[i].c;
    sort (a+1,a+1+n);
    sort (d+1,d+1+m,cmp);

    long long sum=0,s,best=-1;
    if( a[n] < d[m].b ) cout << "-1" << endl;
    else 
    {
        for (int i=m;i>=1;i--)
        {
            sum=sum+d[i].c;
            s=n+1-(lower_bound(a+1,a+1+n,d[i].b)-a);
            if(sum%s==0)
            {
                best = max(best,sum/s);
            }
            else 
            {
                best = max(best,(sum/s+1));
            }
        }
        cout << best << endl;
    }

return 0;
}

二分查找和二分答案

【算法1-6】二分查找与二分答案 - 题单 - 洛谷

二分查找

int Search(int num,int low,int high)
{
    int mid;
    while(low<=high)
    {
        mid=(low+high)/2;
        if(num>=b[mid])  low=mid+1;
        else   high=mid-1;
    } 
    return low;   
}

二分答案

使用二分答案技巧的条件:

1)命题可以被归纳为找到使得某命题P(x)成立(或不成立)的最大(或最小)的x

2)把P(x)看作一个值真或假的函数,那么它一定在某个分界线的一侧全为真,另一侧全为假

3)可以找到一个复杂度优秀的算法来检测P(x)的真假

通俗来讲,二分答案可以用来处理“最大的最小”或“最小的最大”问题

模板:

最大值最小化

while(l<r)
{
    int mid=(l+r)>>1;
    if(check(mid))
    {
        r=mid;
    }
    else
    {
        l=mid+1;
    }
}


最小值最大化

while(l<r)
{
    int mid=(l+r+1)>>1;//注意这里是l+r+1
    if(check(mid))
    {
        l=mid;
    }
    else
    {
        r=mid-1;
    }
}


小数二分模版

while(r-l>eps)//eps为精度
{
    int mid=(l+r)>>1;
    if(check(mid))
    {
        r=mid;
    }
    else
    {
        l=mid;
    }
}



判断可行性
 

//这里已经得到l
if(ok(l))
{
    yes;
}
else
{
    no;
}

例题

P1102 A-B 数对

A-B 数对 - 洛谷

这题绝了 好多种优秀的做法(映射 二分 hash 桶 )

Aggressive cows(c语言)_克里斯蒂亚诺.CR7的博客-CSDN博客

先二分距离 然后放牛判断可不可以

Multiplication Table_Apollo-yyy的博客-CSDN博客

lower_bound()   upper_bound()

c++的lower_bound函数、upper_bound和find函数_无敌少年小旋风的博客-CSDN博客

 

三分法

左右三分之一点

取最大值为例->设小区间

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值