暑假算法刷题日记 Day 2

006 、激光炸弹

题目描述

链接:https://ac.nowcoder.com/acm/problem/20032
来源:牛客网

一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。
现在地图上有n(N ≤ 10000)个目标,用整数Xi,Yi(其值在[0,5000])表示目标在地图上的位置,每个目标都有一个价值。
激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。
若目标位于爆破正方形的边上,该目标将不会被摧毁。

解题思路

读完题之后,感觉是一道很明显的二维前缀和的题目。因为通过枚举的话,枚举起点5000 * 5000,还要计算正方形内的价值和,r * r是5000 * 5000,复杂度达到了50004,已经超了10 8,可能会超时间。
所以我们采用前缀和。省去每次重复计算正方形内的价值和,就把复杂度降到了50002

题目亮点

单纯模板题

代码

#include<iostream>
using namespace std;

int  n,r;
const int N =  1e4 + 10;
int a[5010][5010];
int pre[5010][5010];
int ans;

int main(){
    cin>>n>>r;
    int x,y,v;
    for(int i=1;i<=n;i++){
        cin>>x>>y>>v;
        a[x+1][y+1]=v;
    }
    //计算二维前缀和
    for(int i=1;i<=5000;i++){
        for(int j=0;j<=5000;j++){
            pre[i][j]=pre[i-1][j]+pre[i][j-1]+a[i][j]-pre[i-1][j-1];
        }
    }
    //枚举起点,比较答案
    for(int i=r;i<=5000;i++){
        for(int j=r;j<5000;j++){
            int temp = pre[i][j]-pre[i-r][j]-pre[i][j-r]+pre[i-r][j-r];
            ans=max(temp,ans);
        }
    }
    cout<<ans;
}

007 、激光炸弹

题目描述

链接:https://ac.nowcoder.com/acm/problem/207053
来源:牛客网

我们刚刚学了二分查找——所谓二分查找就是在一堆有序数里找某个符合要求的数。在学完二分查找之后如果让你玩猜数游戏(裁判选定一个目标数字,你说一个数裁判告诉你是高了还是低了直到你猜到那个数)的话,显然你会用二分的方式去猜。
但是不是每一个玩猜数游戏的人都知道二分是最好,甚至一个健忘的玩家都有可能在得到裁判回答的下一个瞬间就忘了他之前问了什么以及裁判的回答),而现在更可怕的是,这个告诉你猜的数是高还是低的裁判他也很健忘,他总是薛定谔的记得这个目标数字,也就是说他的回答有可能出错。我们已经不关心这个不靠谱的游戏本身了,我们更关心裁判这个薛定谔的记得到底有几个是记得…
现在给出这个健忘的玩家的所有猜测和裁判的所有回答,问裁判最多能有多少次是记得目标数字的,即裁判的回复是符合情况的。

解题思路

思考:第一感觉像区间合并,有最多区间覆盖的区间数目的最大值。那我们可以每次都在给的范围上的数都 +1,最后看数字的最大值就是有最多区间覆盖的区间数目的最大值。其实就是差分,但是数的范围太大,我们需要用map,离散化一下。
思路:读入每次的输入,假如输入是 a ,c
1. c==’ . ’ , 则范围是x=a;
2. c==’ + ’ , 则范围是x<a;
3. c==’ - ’ , 则范围是x>a;
然后每次就insert(l,r,1)进行差分,最后前缀和找最大值。

题目亮点

差分的范围太大,需要用到离散化
因为我们只在乎最大值,所以我们直接遍历map求前缀和就可以得到最大值

注意

0x3f3f3f3f 不是int的最大值,我们只是一般用这个数表示+∞

代码

#include<iostream>
#include<map>
#include<climits>
using namespace std;

const int N = 1e5 +10;
int n;
int a;
char c;
map<long long,int> mp;
int ans=0;
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a>>c;
        switch(c){
            case '.':mp[a]++;
                mp[a+1]--;
                break;
            case '+':mp[a]--;
                mp[-INT_MIN]++;
                break;
            case '-':mp[a+1]++;
                mp[INT_MAX+1];
                break;
        }
       
    }
    int pre=0;
    for(map<long long  ,int>::iterator it = mp.begin();it!=mp.end();it++){
        pre+=it->second;
        ans= max(ans,pre);
        
    }
    
    cout<<ans;
    
}

008 、货仓选址

题目描述

链接:https://ac.nowcoder.com/acm/problem/50937
来源:牛客网

在一条数轴上有N家商店,它们的坐标分别为 A[1]~A[N]。现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。

解题思路

其实这个比较简单,有点初中的数学题。**相当于在直线上找一点,到已知两点的距离之和最小。根据两点之间线段最短可知,只有这个点在这个线段上,才会使距离和最短。**对于这个题,我们就把所有的位置从小到大排序,如果是奇数,就位于中间的那个点;如果是偶数,就位于中间两点之间,这样就可以使得距离之和最小。

代码

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 +10;
int n;
int a[N];

int main(){
    cin>>n;
    int i;
    for(i=0;i<n;i++){
        cin>>a[i];
    }
    int l=0,r=i-1;
    sort(a,a+i);
    long long ans=0;
    while(l<r){
        ans+=a[r--]-a[l++];
    }
    cout<<ans;
}

009 、字符串

题目描述

链接:https://ac.nowcoder.com/acm/problem/18386
来源:牛客网

小N现在有一个字符串S。他把这这个字符串的所有子串都挑了出来。一个S的子串T是合法的,当且仅当T中包含了所有的小写字母。小N希望知道所有的合法的S的子串中,长度最短是多少。

解题思路

先想到的是滑动窗口。只要没有集齐26个字母就扩张,集齐了就收缩寻找最小值。

题目亮点

经典的滑动窗口

代码

#include<iostream>
using namespace std;

string s;
int ans=0x3f3f3f3f;
int t[30];//记录当前窗口中每个字母的个数
//判断是否集齐26个字母
bool isOk(){
    for(int i=0;i<26;i++){
        if(t[i]<=0)return false;
    }
    return true;
}
int main(){
    cin>>s;
    int l=0,r=0;
    while(r<s.size()){
        //扩张
        while(!isOk()&&r<s.size()){
            t[s[r++]-'a']++;
        }
        //判断是否最小
        ans=min(ans,r-l+1);
        //收缩
        while(isOk()&&l<=r){
            t[s[l++]-'a']--;
            ans=min(ans,r-l+1);//更新最小值
        }
        
    }
    cout<<ans;
}

010 、丢手绢

题目描述

链接:https://ac.nowcoder.com/acm/problem/207040
来源:牛客网

“丢丢丢手绢,轻轻地放在小朋友的后面,大家不要告诉她,快点快点抓住她,快点快点抓住她。”
牛客幼儿园的小朋友们围成了一个圆圈准备玩丢手绢的游戏,但是小朋友们太小了,不能围成一个均匀的圆圈,即每个小朋友的间隔可能会不一致。为了大家能够愉快的玩耍,我们需要知道离得最远的两个小朋友离得有多远(如果太远的话牛老师就要来帮忙调整队形啦!)。
因为是玩丢手绢,所以小朋友只能沿着圆圈外围跑,所以我们定义两个小朋友的距离为沿着圆圈顺时针走或者逆时针走的最近距离。

解题思路

仔细一想,好像也可以用滑动窗口来做。收缩和扩张的条件是 是否l和r的距离大于二分之一圆周的长度(因为在圆中,距离最长,就是距离越接近周长的一半,所以我们选取距离大于二分之一圆周长作为收缩条件)。需要注意的是这个是个圆周,首位相接,宽口可以跨过首尾,代码中我们使用%操作进行维护

代码

#include<iostream>
using namespace std;

const int N = 1e5 +10;
int n;
int a[N];
int sum=0;
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
        sum+=a[i];
    }
    int midSum=sum/2;
    int l=0,r=0;
    int temp=0;//记录窗口中当前的距离和
    int ans=0;
    
    while(1){
        //扩张
        while(temp<midSum&&r<n){
            temp+=a[r++];
            r=r%n;
        }
        ans=max(ans,min(temp,sum-temp));
        //收缩
        while(temp>=midSum&&l<n){
            temp-=a[l++];
            l=l%n;
            if(l==0)break;//当左端点l再次回到第一个同学时,意味着所有方案遍历一遍了,就可以退出了
            ans=max(ans,min(temp,sum-temp));
        }
        ans=max(ans,min(temp,sum-temp));
        if(l==0)break;
    }
    cout<<ans;
    
}

结语

若现状非你所愿,请在尘埃落定前奋力一搏!
加油!坚持就是胜利。

请添加图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值