贪心
知识点
局部最优解->整体最优解
选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
证明贪心策略的有效性
· 反证法
· 数学归纳法
例题
P1090 [NOIP2004 提高组] 合并
P3817 小A的糖果
P3817 小A的糖果(贪心算法)_矩阵科学的博客-CSDN博客
#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;
}
牛牛的战役
#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;
}
二分查找和二分答案
二分查找
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 数对
这题绝了 好多种优秀的做法(映射 二分 hash 桶 )
Aggressive cows(c语言)_克里斯蒂亚诺.CR7的博客-CSDN博客
先二分距离 然后放牛判断可不可以
Multiplication Table_Apollo-yyy的博客-CSDN博客
lower_bound() upper_bound()
c++的lower_bound函数、upper_bound和find函数_无敌少年小旋风的博客-CSDN博客
三分法
左右三分之一点
取最大值为例->设小区间