没有任何迭代,故若忽略分配数组空间的时间,共需常数时间。
现在越来越感觉消化吸收比做新的题重要。我可能之前花了很多时间把一个题好像搞清楚了。但是人脑是会遗忘的。忘掉之后,需要重新捡起来。所以得多复习以前学过的内容。今天准备复习一下贪心。贪心就是每一步都用最优解,试图得到一个全局最优解。抽象的这种描述可能大家都会。show me the code
#include
#include
using namespace std;
const int N=1e5+10;//数组大小,以后统一用科学计数法,比较简单
int n;
struct Range{//结构体
int l,r;
bool operator< (const Range& W)const{
return r<W.r;//重载小于号,记得要 return ,刚写忘了,怎么也找不到
}//问题
}range[N];
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
int l,r;
scanf("%d%d",&l,&r);
range[i]={l,r};
}
sort(range,range+n);//按照右端点排序
int ans=0;
int ed=-2e9;
for(int i=0;i<n;i++){
auto r=range[i];
if(r.l>ed){//贪心地选择右端点
ans++;
ed=r.r;
}
}
printf("%d\n",ans);
return 0;
}
#include
#include
using namespace std;
const int N=1e5+10;//非常丝滑,简直完美。但是逻辑是啥呢。为啥这两题的
int n;//代码完全一样。每次选右端点,为什么是正确的。
struct Range{//我们要求的是最大的不相交的区间的数量,要数量最大,
int l,r;//应该就是要让区间尽可能短吧,好像也不是。和别的区间没有交集
bool operator< (const Range& W)const{//的区间一定要选上
return r<W.r;//和别的区间有交集的区间,选一个,对,选尽可能左边
}//因为右边可能有新的区间,假设我们选靠右边的,比如说样例的那个,我们选
}range[N];//5 这个点,假设有一个区间是 5 到 6 ,那么我们就不能得到最优解3 了
//假设我们选 4 这个点,就可以得到 3 这个最优解(我这里是多加了一个区间来帮助理解
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
int l,r;
scanf("%d%d",&l,&r);
range[i]={l,r};
}
sort(range,range+n);
int ans=0;
int ed=-2e9;
for(int i=0;i<n;i++){
auto r=range[i];
if(r.l>ed){
ans++;
ed=r.r;
}
}
printf("%d\n",ans);
return 0;
}
第三题就不会写了。看了一下自己之前的笔记。还是有点帮助。哈哈。区间分组
#include
#include
#include //小根堆的头文件
using namespace std;
const int N=1e5+10;
int n;
struct Range{
int l,r;
bool operator < (const Range &W)const{
return l<W.l;//按照左端点排序
}
}range[N];
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
int l,r;
scanf("%d%d",&l,&r);
range[i]={l,r};
}
sort(range,range+n);
priority_queue<int,vector<int>,greater<int>> heap;//小根堆
heap.push(range[0].r);//把最左边的区间的右端点存进去
for(int i=1;i<n;i++){//枚举剩下的区间
if(range[i].l>heap.top()){//可以放到同一组
heap.pop();//更新这一组的右端点
heap.push(range[i].r);
}else{//不能放到同一组
heap.push(range[i].r);
}
}
printf("%d\n",heap.size());
return 0;
}
感觉也不难了。多亏有之前的笔记。自己看自己的笔记,比看别人的题解更加清晰。因为属于个性化的内容。感觉个性化这个东西非常非常重要。下一个题也不会,但是自己做的笔记不是那么详细,记忆像是消磁了一样。果然好记性不如烂笔头。区间覆盖这个题,结合了双指针。前面两个题是右端点排序,后面两个题是左端点排序。最关键的一个点就是,我们需要更新我们需要覆盖的区间。覆盖掉一段,就把区间的左端点用使用的区间的右端点覆盖掉。我们要找的是最靠近需要覆盖区间的区间。
#include
#include
using namespace std;
const int N=1e5+10;
int n;
int st,ed;
struct Range{
int l,r;
bool operator < (const Range& W)const{
return l<W.l;//左端点排序
}
}range[N];
int main(){
scanf("%d%d",&st,&ed);
scanf("%d",&n);
for(int i=0;i<n;i++){
int l,r;
scanf("%d%d",&l,&r);
range[i]={l,r};
}
sort(range,range+n);
int ans=0;
bool success=false;
for(int i=0;i<n;i++){
int j=i;//双指针
int r=-2e9;
while(j<n&&range[j].l<=st){//找最靠近目标区间左端点的区间
r=max(r,range[j].r);
j++;
}
if(r<st){//找完了都靠近不了左端点,说明失败了
break;
}
ans++;//靠近了左端点,说明 j 这个区间的左端点在目标区间的左端点的
//右边了,这个时候我们下一次枚举要从前面那个区间开始枚举,也就是说
//i=j-1 ,是不是还有点迷糊,举个例子,比如说,3 这个区间在目标区间的右边了
//2 这个区间在目标区间的左边。然后我们会把目标区间的左端点更新为 3 这个区间在目标区间的左边。然后我们会把目标区间的左端点更新
//区间的右端点。那我们下一次循环还是得从 2 这个区间开始循环。相当于我们已经选择了
//3 这个区间。好像不是这个问题,是因为 for 循环 i++ 吧。多加一次会跳过一个
//区间
if(r>=ed){
success=true;
break;
}
st=r;
i=j-1;//有点烧脑,为什么是 j-1 而不是 j 呢。哦哦。因为前面有一个 i++
}
if(!success){
ans=-1;
}
printf("%d\n",ans);
return 0;
}
小根堆
#include
#include
#include
using namespace std;
int n;
priority_queue<int,vector<int>,greater<int>> heap;//小根堆
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
int x;
scanf("%d",&x);
heap.push(x);//把所有的元素存进小根堆
}
int ans=0;
while(heap.size()>1){
int a=heap.top();
heap.pop();
int b=heap.top();
heap.pop();//把最小的两个元素取出来
ans+=a+b;//累加
heap.push(a+b);//更新为新的一堆果子
}
printf("%d\n",ans);
return 0;
}
排队打水
#include
#include
using namespace std;
const int N=1e5+10;
int n;
int a[N];
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
sort(a,a+n);
long long ans=0;//好像会爆 int
for(int i=0;i<n;i++){
ans+=(n-i-1)*a[i];//前面的等待的时间后面都是要计算进去的
}
printf("%lld\n",ans);
return 0;
}
货仓选址
#include
#include
using namespace std;
const int N=1e5+10;
int n;
int a[N];
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
sort(a,a+n);//排序
long long distance=0;
for(int i=0;i<n;i++){
distance+=abs(a[n/2]-a[i]);//选中点作为位置
}
printf("%lld\n",distance);
return 0;
}
牛
#include
#include
using namespace std;
const int N=5e4+10;
int n;
struct Cow{//结构体按照 重量+强壮程度 排序
int total,weight,strength;
bool operator< (const Cow& W)const{
return total<W.total;
}
}cow[N];
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
int weight,strength;
scanf("%d%d",&weight,&strength);
int total;
total=weight+strength;
cow[i]={total,weight,strength};
}
sort(cow,cow+n);//排序
int ans=-0x3f3f3f3f;//初始化为负无穷
int total_weight=0;//当前牛的上面的所有牛的重量
for(int i=0;i<n;i++){
ans=max(ans,total_weight-cow[i].strength);
total_weight+=cow[i].weight;//更新上面所有牛的总重量
}
printf("%d\n",ans);
return 0;
}
总共是八个题,前面四个相对难一些,后面四个题比较直观。然后新的一些数据结构就是结构体和小根堆。贪心的证明其实不用太管。能理解这个贪心的思想就好了。