POINT 1 贪心

每一步都取当前的最优解的思想;一般来说符合直观思路,需要严格的证明;OI中使用多个错误的贪心策略进行加成有时会有良好的效果

例一
给定N个农民,第i个农民有Ai单位的牛奶,单价Pi
现在要求从每个农民手中购买不超过Ai单位,总共M单位的牛奶。求最小花费 
题解:

将将所有农民按单价排序,能取就取即可

例二
给定n个物品,第i个物品有大小li,要求将这些物品装进容积为L的箱子里,每个箱子至多放两个物品,求最少所需箱子数。

1 ≤ n ≤ 105

题解:

将物品从大到小排序考虑当前的最大物品,假设能与最小值凑成一对,就凑成一对
 
否则必然不存在一个物品能与他凑成一对,因此单列用双指针维护这个过程即可

例三
有n个物品,每个物品有属性Ai和Bi。你需要将他们排成一行 
如果物品i被放在第j个位置,那么会产生代价Ai · (j - 1) + Bi · (n - j)
现在要求总代价的最小值
1 ≤ n ≤ 105

展开式子
得到ans = ΣAi · j - Ai + Bi · n - Bi · j
发现 Bi · n - Ai 是常数,会变化的只有Ai · j - Bi · j
因此按Ai - Bi排序即可

大的放前面,小的放后面

例四
给定n个水龙头,第i个水龙头有最大出水量Ai,且给定一个温度值ti。

定义一次出水得到的温度为Σ(Ai *ti)/Σ(Ai) ,给定一次出水得到的温度T,求最大总出水量。

如果得不到该温度,输出0
1 ≤ n ≤ 2 * 105, 0 ≤ Ai, ti ≤ 106

题解:
先把ti减去T,然后按照t排序
把数组分成两块,一半小于等于0,一半大于0
用贪心的思想,可以发现有一半必须全选,另一半选最靠近T的那些

代码:

#include<cstdio>
#include<algorithm>
#include<cstring> #define N 300005 using namespace std; int a[N],t[N],i,j,m,n,p,k,id[N],ID[N],sum[N],T; double ans; int cmp(int x,int y)
{
return sum[x]<sum[y];
} int main()
{
scanf("%d%d",&n,&T);
for (i=;i<=n;++i)
{
scanf("%d",&a[i]);
}
for (i=;i<=n;++i) scanf("%d",&t[i]);
for (i=;i<=n;++i)
{
if (t[i]==T) ans+=a[i];
else if (t[i]<T) id[++id[]]=i,sum[i]=T-t[i];
else ID[++ID[]]=i,sum[i]=t[i]-T;
}
sort(id+,id+id[]+,cmp);
sort(ID+,ID+ID[]+,cmp);
long long suma=,sumb=;
for (i=;i<=id[];++i)
suma+=1ll*sum[id[i]]*a[id[i]];
for (i=;i<=ID[];++i)
sumb+=1ll*sum[ID[i]]*a[ID[i]];
if (suma<sumb)
{
swap(suma,sumb);
for (i=;i<=n;++i) swap(ID[i],id[i]);
}
for (i=;i<=ID[];++i) ans+=a[ID[i]];
for (i=;i<=id[];++i)
if (1ll*sum[id[i]]*a[id[i]]>=sumb)
{
ans+=.*sumb/sum[id[i]];
break;
}
else
{
ans+=a[id[i]];
sumb-=1ll*sum[id[i]]*a[id[i]];
}
printf("%.10lf\n",ans);
}

证明:
假设负数集里面还有一些没选,正数集里还有数剩余
那么我们就可以把他们凑出一个0出来,直到某一边用完为止.证毕.

所以就可以直接贪心了

例五
有n个闹钟,第i(1 ≤ i ≤ n)个闹钟将在第ai(1 ≤ ai ≤ 106)分钟鸣响,鸣响时间为一分钟。当在连续的m分钟内,有至少k 个闹钟鸣响,则会被叫醒。

现要求关闭一些闹钟,使得在任意连续的m分钟内,鸣响的闹钟数量恒小于k。

题解:

一个直观的想法是,我们按照重叠最多的顺序排序,但是这样是有问题的,毕竟如果这么一排,就会导致左右两边分开,反而会使情况更糟。

但是如果我们从左往右扫就不一样了。只要碰到会吵醒的情况,我们就弹出,这样能够保证区间是连续的。

并且,由于区间一定会出现问题,所以我们不能就此放置不管我们选择最右面的关闭,因为这样会让剩余的能关的更少

POINT 2 二分

二分的思想
给定一个单调的函数/数组,给定一个值,求这个值是否存在,或者找到这个值应当存在的位置

由于数组有序,不妨认为他单调递增
假设Ai > x,则必然有∀j > i, Aj > x
假设Aj < x,则必然有∀j < i, Aj < x
二分的原理就是每次在待定区间中选择mid。
必然可以确定一边是没有意义的。每次问题的规模缩小 1/2
因此复杂度为O(logN)

寻找<=x的第一个位置

如果两次二分找到的中点一样的话,就说明已经二分完了

#include<cstdio>
#include<algorithm>
#include<cstring> #define N 300005 using namespace std; int i,j,m,n,p,k,a[N],x; int check(int x)
{
int i,cnt=;
for (i=;i<=n;++i) if (a[i]-a[i-]>x) return ;
for (i=;i<n;)
{
for (j=i;j<=n&&a[j]-a[i]<=x;++j);
++cnt;
i=j-;
}
if (cnt<=m) return ;
return ;
} int main()
{
scanf("%d%d",&n,&m);
for (i=;i<=n;++i) scanf("%d",&a[i]);
sort(a+,a+n+);
int l=,r=(int)1e9,mid=;
while ((l+r)>>!=mid)
{
mid=(l+r)>>;
if (check(mid)) r=mid;
else l=mid;
}
printf("%d\n",r);
}

最后是答案是存在变量L中

注意一定要是单调序列

二分答案

顾名思义,就是对答案进行二分
对于某些要求“满足某条件的最小值”类的问题,对答案进行二分,假设答案不超过mid,则问题变为“满足某条件且某值不超过mid”的判定性问题。
常用于最大值最小化类问题。

在二分答案之后往往需要一个贪心策略。

例一
一条河上有n个石子排成一条直线,第i个石子距离河岸xi。一只年长的青蛙想要从岸边(x=0处)到达第n个石子上(其实是对岸)。这只青蛙实在是太年长了,所以最多只能

跳m次,而且他希望他这些次跳跃中距离最远的那次距离尽可能的短。请你帮他求出这个最远距离最短能是多少。

1 ≤ m ≤ n ≤ 105

最小化:最大的跳跃距离
二分答案:设答案为mid,则问题变为:
n个石子,只能跳m次,每次跳远距离不能超过mid,问是否可行。

或者n个石子,每次最远距离不超过mid,问最少跳多少次(然后和m比较即可)。

贪心策略:每次跳的尽量远即可

二分O(logN)*贪心O(N)=O(NlogN)

先检查是否能跳的过去

再让他在不超过最远距离的情况下多跳

如果他跳的步数不大于m就可行

例二
给定n个物品,每个物品有属性Ai和Bi。要求在其中选择k个物品,使得选择的物品,的sum(A)/sum(B)尽可能大。

贪心:选Ai/Bi最高的k个物品?
反例:
3 2
1000 10
1000 100
1 1
除了最优的物品一定会选之外 可以考虑选择Bi非常小的物品, 减小对性价比的影响。此时物品3比物品2更优。

二分答案
假设sum(Ai)/sum(Bi) >= mid
则:sum(Ai) - mid * sum(Bi) >= 0
即:sum(Ai-mid*Bi) >= 0
将Ai-mid*Bi作为第i个物品的权值,问题变为能否选k个物品使得权值和大于0.此时贪心选择权值最大的k个物品即可。
二分O(logN)* 排序O(NlogN) = O(Nlog 2N)

二分是对一个单调的函数进行的操作
那么我们有没有办法对一个单峰的函数进行操作呢?
求一个单峰函数的极值点

三分函数

三分

发现共性:l,r中值较小的那一段一定会被舍去严格的实现每次都能缩小问题的 1/3

事实上我们取两次mid会好写很多,只是常数问题

#include<cstdio>
#include<algorithm>
#include<cstring> #define N 500005 using namespace std; int i,j,m,n,p,k,a[N],ty,x; long long b[N]; double check(int x)
{
return .*(b[x-]+a[n])/x;
} int main()
{
scanf("%d",&m);
for (;m--;)
{
scanf("%d",&ty);
if (ty==)
{
scanf("%d",&x);
a[++n]=x;
b[n]=b[n-]+x;
}
else
{
int l=,r=n;
while (r-l>)
{
int len=(r-l+)/,mid1=l+len,mid2=mid1+len;
if (check(mid1)<check(mid2)) r=mid2;
else l=mid1;
}
double ans=;
for (i=l;i<=r;++i) ans=max(ans,a[n]-check(i));
printf("%.10lf\n",ans);
}
}
}

例一
初始有一个为空的集合,要求支持两种操作
1.不断向集合中插入一个数,且这个数比集合中所有数都大
2.在集合中找一个子集,使得找到的子集S中的最大值减去子集S中元素的平均值的差最大,并输出这个差

操作数≤ 500000

最大值肯定要选,可以自己证明一下

然后使其他数尽可能小

如何选取子集?
最后插入的这个数是一定要选的,然后再选小的数,就是一个最大数加上几个用来拉低平均值的小数构成了所需子集

小数一定是从最小值开始连续增加使平均值减小,直到达到一个临界点,再增加小数就会使平均值增大,易知这是一个单峰函数

因此考虑三分选多少小数即可

题解:

让上线稍微大一些

check:取前k个的和

(前x-1小的数+a[n])/x

main:

其他的三分就是更改check函数

分治的思想
将一个问题划分成若干个(一般都是分成俩)子问题
分别解决每个子问题后(也可能是前,还可能一前一后之类
的)
将各个子问题组合起来得到原问题的答案。

快速幂
如何快速计算X k

我们将k进行二进制拆分。
比如我们需要计算X 11即我们需要计算X 20+21+23
因此我们只需要计算logk 次即可

归并排序
基本思想:先将整个数组分成两个部分,分别将两个部分排好序,然后将两个排好序的数组O(n)合并成一个数组。

我们将问题分为两个阶段:分、治


对于每个长度> 1的区间,拆成两个[l, mid]区间和[mid + 1, r]区间

直接递归下去


我们认为在处理区间[l,r]时,已经有[l,mid]和[mid+1,r]内分别有序

这一次的操作就是合并两个有序序列,成为一个新的长有序序列

用两个指针分别指向左右分别走到哪了即可

比较两个指针指向的值

复杂度O(nlogn)是一个严格的算法

逆序对
给定一个1 ∼ n的排列,求逆序对数量。
1 ≤ n ≤ 105
逆序对:对于1 ≤ x < y ≤ n, 若A[x] > A[y],则称(x,y)为一个逆序对。

题解
首先显然我们枚举x,y可以做到O(N2)
分治:
假设当前问题 Work(l,r) 是求l到r区间内的逆序对数量。
讨论所有(x,y)可能在的位置:
l ≤ x < y ≤ mid :子问题Work(l,mid)
x ≤ mid < y : ???
mid + 1 ≤ x < y ≤ r :子问题Work(mid+1,r)

对于每个mid右边的数,我们要找到mid左边有多少比它大的数。

1) 对左侧排序,右侧在左侧上二分即可。 总时间复杂度O(nlog2n)

2) 归并排序:
对于数组A和数组B的归并过程,每当我们将B中的元素取出时:
说明A中最小的元素比该元素大:说明A中所有元素比该元素大:说明 答案+=A.size()

归并过程时间复杂度O(n),总时间复杂度O(nlogn)。

例二
有一个序列,初始时只有一个数n
对于序列中每一个> 1的数,拆分成三个数n/2,n%2,n/2并替换原数。
直到序列中没有> 1的数为止
查询最终序列中[l, r]中有多少1
0 ≤ n < 250, 0 ≤ r - l ≤ 105

平面最近点对
给定二维平面上的N个点,求任意两点间的最近距离(欧几
里得距离)。
1 ≤ n ≤ 105

题解
不妨按照x坐标排序。对于区间[l,r],我们将其分成mid左右
两个部分。
两个点都在左侧:子问题Work(l,mid)
两个点都在右侧:子问题Work(mid+1,r)
两个点一个在左侧,一个在右侧 :

不妨按照x坐标排序。对于区间[l,r],我们将其分成mid左右
两个部分。
两个点都在左侧:子问题Work(l,mid)
两个点都在右侧:子问题Work(mid+1,r)
两个点一个在左侧,一个在右侧:
重点考虑第三种情况

不妨假设左右两个子问题的答案为ans。则我们只需要考虑
分界线两边距离不超过ans以内的点即可。

不妨假设左右两个子问题的答案为ans。则我们只需要考虑
分界线两边距离不超过ans以内的点即可。

不妨假设左右两个子问题的答案为ans。则我们只需要考虑
分界线两边距离不超过ans以内的点即可。

对于每个点,可能和它距离不超过ans的点坐标范围
横坐标:[mid-ans,mid+ans]
纵坐标:[y-ans,y+ans]

每个小正方形内点数不可能超过一个(因为任意两点距离不
低于ans)。故总点数不超过6个。除去该点自身,该点至多
需要和其他6个点求距离。
故该部分复杂度不超过O(n)。实现时可以直接对所有点按
照y坐标排序,O(n log2 n),或者使用归并排序的技巧,直
接O(n log n)即可。

Day 1 下午的更多相关文章

  1. 搞了我一下午竟然是web.config少写了一个点

    Safari手机版居然有个这么愚蠢的bug,浪费了我整个下午,使尽浑身解数,国内国外网站搜索解决方案,每一行代码读了又想想了又读如此不知道多少遍,想破脑袋也想不通到底哪里出了问题,结果竟然是web.c ...

  2. System.DateUtils 3. IsPM、IsAM 判断是否为上、下午

    编译版本:Delphi XE7 function IsPM(const AValue: TDateTime): Boolean; inline;function IsAM(const AValue: ...

  3. 用一个下午从零开始搭建一个基础lbs查询服务

    背景 现在做一个sns如果没有附近的功能,那就是残缺的.网上也有很多现成的lbs服务,封装的很完整了. 我首先用了下百度lbs云,但是有点不适合自己的需要,因此考虑用mongodb建一个简单的lbs服 ...

  4. 新蒂下午茶体基本版SentyTEA-Basic

    一.目前的最新版新蒂下午茶体包含了7600+常用汉字,每个字都是手写而成,是一套充满手写感的中文字体,轻松.惬意,如同慢饮一杯下午茶.SentyTEA-Basic.ttf  这个一个新蒂下午茶体基本版 ...

  5. JAVA判断当前时间是上午am还是下午pm

    //结果为"0"是上午 结果为"1"是下午 public class GregorianTest { public static void main(Strin ...

  6. PKUSC 模拟赛 day2 下午总结

    终于考完了,下午身体状况很不好,看来要锻炼身体了,不然以后ACM没准比赛到一半我就挂掉了 下午差点AK,有一道很简单的题我看错题面了所以没有A掉 第一题显然是非常丝薄的题目 我们很容易通过DP来O(n ...

  7. PKUSC 模拟赛 day1 下午总结

    下午到了机房之后又困又饿,还要被强行摁着看英文题,简直差评 第一题是NOIP模拟赛的原题,随便模拟就好啦 本人模拟功力太渣不小心打错了个变量,居然调了40多分钟QAQ #include<cstd ...

  8. CTO俱乐部下午茶:技术团队管理中的那些事儿

    摘要:"CTO下午茶"是一种有效的集体对话的模式,参加活动的成员在真诚互动和共同学习的宗旨下齐聚一堂,在喝茶聊天氛围下交流工作心得.本期"CTO下午茶"的主题是 ...

  9. 6月27日CTO俱乐部下午茶印象

    作者:朱金灿 来源:http://blog.csdn.net/clever101 感谢CSDN的邀请,有幸参加了6月27日“CTO俱乐部下午茶时光:CTO在团队管理中所遇到的那些事”活动.本期的主讲嘉 ...

  10. 写了一下午的dijkstra。突然发现我写的根本不是dijkstra。。。。是没优化过的BFS.......

    写了一下午的dijkstra.突然发现我写的根本不是dijkstra....是没优化过的BFS.......

随机推荐

  1. ubuntu:xxx is not in the sudoers file. 问题解决

    ubuntu 下普通用户用 sudo 执行命令时报 "xxx is not in the sudoers file.This incident will be reported" ...

  2. 【带着canvas去流浪】(2)绘制折线图

    目录 一. 任务说明 二. 重点提示 三. 示例代码 3.1 一般折线图 3.2 用贝塞尔曲线绘制平滑折线图 四. 大数据量场景 示例代码托管在:https://github.com/dashnowo ...

  3. 学代码第十七天,JAVA继承

    JANA面向对象的三大特性:封装,继承,多态. 今天学了继承,继承,通俗点说就是子类可以用父类的代码,或重写父类的方法.构造方法.属性 例如我这里要调用父类的方法: 下边有两个测试类,自己分别试一下, ...

  4. Mysql使用event,类似oracle job

    MySQL从5.1开始支持event功能,类似oracle的job功能.有了这个功能之后我们就可以让MySQL自动的执行数据汇总等功能,不用像以前需要操作的支持了.如linux crontab功能. ...

  5. vue+element-ui实现行数可控的表格输入

    element的table中使用 <template slot-scope="scope"> </template> 包裹想要插入的input,或者sele ...

  6. 安卓开发:初识Android Studio

    配置:Android Studio3.2.0,gradle-4.6 ,windows10  一.Android Studio安装 在http://www.android-studio.org/完成下载 ...

  7. 在项目管理中如何保持专注,分享一个轻量的时间管理工具【Flow Mac版 - 追踪你在Mac上的时间消耗】

    在项目管理和团队作业中,经常面临的问题就是时间管理和优先级管理发生问题,项目被delay,团队工作延后,无法达到预期目标. 这个仿佛是每个人都会遇到的问题,特别是现在这么多的内容软件来分散我们的注意力 ...

  8. C学习笔记(逗号表达式)

    (1)书写: ① int i; i=(i=*,i*); printf("%d\n",i); i=60; ② int i; i=i=*,i*; printf("%d\n&q ...

  9. 关掉那些windows上因权限无法关闭的服务

    ProcessExplorer 下载地址 :https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer 以管理员身 ...

  10. Java BigDecimal类型的 加减乘除运算

    原文: https://blog.csdn.net/xuwei_net/article/details/81253471 加法:add 减法:subtract 乘法:multiply 除法:divid ...