飞道的博客

黑龙江农垦科技职业学院喜迎寒假多校联赛2(快乐ak场)

432人阅读  评论(0)

快乐ak场 (没做可以做一下 - > 本次比赛链接

  • 前言
    这次比赛难吗,我认为挺容易的(每道题都挺容易的)。
    我后面一个半小时都在玩,我感觉我就是个撒比,这种难度都做不来(A 和 D 没做),还不好好看题,我感觉自己真的挺无语的,重点是考的都是我已经学过的,没有盲区,害,萌新直接去世,脑子瓦特。

    希望大家一起加油,好好努力,发现不足,及时补进

  • 第一题 数组截取

思想:双指针 + 快速读取

  • 快速读取模板
inline int read() {
   
		char c = getchar(); int n = 0;
		while (c < '0' || c > '9') {
    c = getchar(); }
		while (c >= '0' && c <= '9') {
    n = (n << 1) + (n << 3) + (c & 15); c = getchar(); }
		return n;
 	}                 ///快读模板

这个直接背就行了,不需要什么思想。

  • 快写模板
void write(int x)
	{
   
		if(x<0)	putchar('-'),x=-x;
		if(x>9)	write(x/10);
		putchar(x%10+'0');
	}           ///快写模板

好了正式开始,讲双指针。
传送门 - > 题目A,点击这里

  • 解析

    双指针思想就是用两个指针,指向一个区间,主要分两种
    第一种:同时指向开头 (这题)
    第二种:分别指向开头和结尾


    举个例子,让 i 和 j 两个指针同时指向 【3,3,1,1,1,0,0,0,3,3】这个区间开头
    让 j 这个指针不断右移,直到找到 j 到 i 的区间里面之和大于 k (例题就是 3)

    这里的 i 和 j 这个范围区间为6,我们就让 i 这个指针右移

    这样就区间就之和 就不大于 3(也就是k) 然后进行判断区间是否等于 k ,如果等于就记录距离。
    以此类推,输出我们找到最大的距离

  • 注意事项

    1 题目数据很大(超过了题目规定的范围),所以开大点数组
    2 快读
    3 long long 类型


代码如下

#include<iostream>
//全开longlong,保险
using namespace std;
inline long long read() {
   
		char c = getchar(); long long n = 0;
		while (c < '0' || c > '9') {
    c = getchar(); }
		while (c >= '0' && c <= '9') {
    n = (n << 1) + (n << 3) + (c & 15); c = getchar(); }
		return n;
 	}
long long a[20000010];
int main(){
   
    long long  n,m,ans=0,sum=-1;
    n=read(),m=read();//快读
    for(long long i=0,j=0;i<n;i++){
   
        a[i]=read();//快读
        ans+=a[i];//累加区间和
        while(ans>m) ans-=a[j++];// 当ans>m时,j 指针右移
        if(ans==m) sum=max(sum,i-j+1); //等于记录距离
    }
    cout<<sum;//输出最大距离
    return 0;//收工
}

想练一道双指针的题吗?
练习题 传送门 - > 快去完成吧


第二题 群友们在排列数字

  • 主要思想:DFS

这 - > 题目

这题本来要用DFS的,可是这属于全排列的范畴,也可以用STL里的全排列神器

next_permutation 不是很了解的 传送门 -> next_permutation详细用法

这道题如果使用神器的话就太简单了,把区间里的数全累加起来取模就行,判断次数

  • 第一种题解 STL的做法

#include<iostream>
#include<algorithm>
using namespace std;
int n,k;
long long a[15];
int main(){
   
    int ans=0;
    cin>>n>>k;
    for(int i=0;i<n;i++) a[i]=i;
    do{
   
        long long sum=0;
        for(int i=0;i<n;i++){
   
            sum=sum*10+a[i];
        }
        if(sum%k==0) ans++;
    }while(next_permutation(a,a+n));
    if(ans) cout<<ans;
    else cout<<-1;
    return 0;
}

再说一下DFS的做法(学过的同学可以看一下,没学过的,学完再看吧,可以看第一种解法)
这题是DFS入门题没啥子好讲(真的就是入门题,DFS新手村的题目)

  • 没有注意事项

代码

#include<iostream>

using namespace std;

const int N=15;
int p[N];
bool s[N];
int n,k;
long long ans;
void dfs(int u){
   
    if(u==n){
   
        long long sum=0;
        for(int i=0;i<n;i++){
   
            sum=sum*10+p[i];
        }
        if(sum%k==0&&sum) ans++;
        return ;
    }
    for(int i=0;i<n;i++){
   
        if(!s[i]){
   
            p[u]=i;
            s[i]=true;
            dfs(u+1);
            s[i]=false;
        }
    }
}
int main(){
   
    cin>>n>>k;
    dfs(0);
    if(ans) cout<<ans;
    else cout<<-1;
    return 0;
}

欢迎看看我的DFS博客 传送门 - > DFS讲解
DFS这题 原型传送门 - > 排序问题


第三题 gg查成绩

主要思想:前缀和

这题又是前缀和新手村的题目(入门题,学了前缀和就会)
如果用枚举,会超时的哟

  • 简单讲一下 前缀和

有两个数组 a[ N ]和s[ N ];
a[ N ]是数据,s[ N ]是a[0]到 a[N] 的区间和
公式 s[ i ]=s[ i-1 ]+a[ i ]
求一段区间 [ l , r ] 和 ans = s[ r ] - s [ l - 1];
这种求法时间复杂度是O(1)的(非常快)

  • 前缀和公式
S[i] = a[1] + a[2] + ... a[i]    //s[i] 数组
a[l] + ... + a[r] = S[r] - S[l - 1]  //求区间和

代码如下

#include<iostream>

using namespace std;
const int N=1e6+10;
int n,m;
long long a[1000005]; //这里我只开了区间和数组,也就是s[N],因为这题不需要用到a[N];
int main(){
   
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        // i 要从1开始避免特判  i=0, i-1=-1,这就错误了 a[0]=0;
        long long x;
        scanf("%lld",&x);
        a[i]=a[i-1]+x;   //累加,得前缀和数组
    }
    for(;m;m--){
       
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%lld\n",a[r]-a[l-1]); 
    }
    return 0;
}

本题原型 赶紧秒了他 - > 前缀和入门题


第四题 issue与lifehappy给学生分组

主要思想: 二分答案

题目 这里

这题二分有点不好写,D和E算是这里水平还可以的题目了
二分我就不多介绍了,就是 对半取

  • 解析


就是用答案证结论 看是大还是小了 然后就相当应缩短区间

主要是怎么取很重要

比方说我们有一个大小为 mid 最大值

比 mid 大的区间就不成立 我们就要再给一组 ,看我们最终的组和题目给我们的组数的大小,来缩短查找区间


如果mid为9,我们就这么划分区间,我只能讲到这里,具体看代码吧,说的比较抽象

代码

#include<iostream>

using namespace std;
const int N=2e6+10;
long long n,m;
long long a[N];
bool f(long long x){
   
    long long ans=0,len=0;//len是组数
    for(int i=0;i<n;i++){
   
        if(ans+a[i]<=x) ans+=a[i];  
        else len++,ans=a[i]; //再划分一个区间
    }
    if(ans) len++; //没划分的数的再划分一个区间
    return len>m;
}
int main(){
   
    cin>>n>>m;
    for(int i=0;i<n;i++) scanf("%lld",&a[i]);
    long long l=0,r=1e11+1;
    while(l<r){
   
        long long mid=l+r>>1;//二分
        if(f(mid)) l=mid+1; //组多了 就增大最大值
        else r=mid; //否则缩小最大值
    }
    cout<<l;
    return 0;
}

这里我们一定要处理好临界情况,整数二分最难搞的就是临界情况

给大家一个二分题,练练手吧 - > 整数二分
再来个浮点二分 - > 浮点二分

模板放下面 自己看

  • 整数二分
bool check(int x) {
   /* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
   
    while (l+1 < r)
    {
   
        int mid = (l + r) 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid ;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
   
    while (l < r)
    {
   
        int mid = (l + r + 1)1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}
  • 浮点二分
bool check(double x) {
   /* ... */} // 检查x是否满足某种性质

double bsearch_3(double l, double r)
{
   
    const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求
    while (r - l > eps)
    {
   
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

这里有STL里有关于二分的函数 分别是 lower_bound( ) 和 upper_bound( ) ,这两个只能用在有序数组里,切记

关于lower_bound( )和upper_bound( )的常见用法

好了下午再补充,我先休息了,嘻嘻


第五题 删删删越小越好

题目

主要思想:单调栈

肯定有人说,这怎么可以是单调栈呢?其实他是字符的单调栈,并非数字,原理是一模一样的。

1 怎么样得到最小值

因为我们有k次删除机会,但我们不管怎么删,数的位数是不会变的
所以我们只需要把高位的数尽可能的小就行了
这时就要用到单调栈思想了

2 啥是单调栈

其实很简单,就是我们要得到一个单调递增的区间,把捣乱的数据删除。

例如 如图

我们有十次机会删除 我们要让前面的位数呈单调递增,这样高位就是最小的。
12345897 到 7 时 不呈单调递增,所以我们把 7 前面的 8和 9 删掉

就变成了123477了,我们现在还有8次删除权限,后面读入1,就变成了1234771,又不呈现单调了,现在我们就要把2,3,4,7,7删了

**就变成了11了,现在我们有三次权限,我们再读入1187,这时把 8删了,我们还有2次删除机会,
以此类推,直至呈现单调递增或删除权限没有了,为止。

这个例题得到的数为 1114164979 因为删除权限用完了,所以我们就输出**

挺简单的,但要想到单调栈的思想很难。

代码如下

#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e7+10;
long long k,tt,hh=1;
char a[N],b[N];//这里位数太大,必须用字符来输入
int main(){
   
    scanf("%s%lld",a,&k);
    for(int i=0;a[i];i++){
   
        while(k&&tt&&b[tt]>a[i]) k--,tt--; //k用完或前面没有字符,我们就不进行删除
        b[++tt]=a[i]; //满足单调性质,就读入
    }
    while(b[hh]=='0'&&hh<tt) hh++; //消除前导零,hh为开头第一个非零的字符
    for(int i=hh;i<=tt-k;i++) cout<<b[i]; //若已经呈单调,但k没有用完
	   return 0;						//我们就输出前hh到tt-k    
}							                       

题目还是要练一下的 - > 单调栈


第六题 happy的异或运算

思想:懂异或,就能做出来了,很简单的思维题

啥是异或?

异或符号为 ^,这个在计算机的主要功能很多,你们自己可以去看,我这里讲简单点。
了解一下这三个运算
1 ^ 0 = 1
0 ^ 0 = 0
1 ^ 1 = 0

知道这三个运算就够了,因为计算机是二进制存储的,所以只有0 和 1 的运算
这题要求最大值 我们只要让所有二进制位都为一就是最大值了
因为 1 到 n 一定可以找到 两个数 1 和 0 互补,使所有位置上都为一,这就是这题思想

例如 4 = 100(二进制), 最大值就为 7 = 111(二进制)

代码如下,提供两种写法

第一种是我写的

#include<iostream>
  
using namespace std;
long long n,sz=0; 
int main(){
   
    cin>>n;
    if(n==1){
   
    	cout<<0;   //因为1的异或最大值为0,所有特判一下,1^1=0 
    	return 0;
    }
    while(sz<=n) sz=(sz*2+1);
    cout<<sz<<endl;  
    return 0;
}

第二种是位运算

#include<iostream>
  
using namespace std;
long long n;
int main(){
   
    cin>>n;
    long long b=1;
    while(b<=n) b<<=1; //使用右移运算符
    cout<<b-1;
    return 0;
}

思维题就没啥可推荐的,一抓一大把。0.0


第七题 Alan%%%
题目

思维:他要求啥,就做啥

这题没啥可讲的,就讲几个string库的小知识吧

1 find函数 找到你想要的字符串位置
例如 字符串 string str = “abcd”
str.find(“cd”),他就会返回cd字符串的位置,位置为2.

2 getline函数
gets没啥区别,作用是读入一行字符串,包括空格,直到碰到回车为止

3 因为c++的string重载了+运算符

   string a = "123"
   string b = "456"
 	string c = a+b
   c=="123456"

代码如下

#include<iostream>

using namespace std;
string a;
int n,ans;
int main(){
   
    cin>>n;
    cin.ignore();//吃掉回车,防止getline吃了上一行的回车
    for(;n;n--){
     
        string b;
        bool flag=false;
        int sum=0;
        getline(cin,a); //读入
        for(int i=0;i<a.size();i++){
   
            if(a[i]!=' ') b+=a[i]; //将空格屏蔽
            if(a[i]=='%') sum++;	//数 % 个数
        }
        if(b.find("Alan")<b.size()) flag=true; //用find函数找一下Alan,如果不在这个范围,就说明没有
        if(flag) ans+=sum;
    }
    cout<<ans;
    return 0;
}

代码题没啥可讲


第八,九题 cg写项目

思维:要求排序

这里我们要介绍sort的自定义写法

就是自己定义比较规则
第一种 STLsort的自定义

#include<iostream>
#include<algorithm>
using namespace std;
struct ss{
   
    string t,m,x,h;    //用户名t,密码m,性别x,电话h
    int id;
}s[100005];
int n;
bool cmp(ss a,ss b){
               //这里我们定义了比较的规则
    if(a.t.size()!=b.t.size()) return a.t.size()<b.t.size();  // t的大小不同,这t的大小 小的在前
    if(a.t!=b.t) return a.t<b.t;    //t的大小相同则比较t,t小的在前
    return a.id<b.id;          //若都相同,这id小的在前
}
int main(){
   
    cin>>n;
    for(int i=0;i<n;i++) s[i].id=i,cin>>s[i].t>>s[i].m>>s[i].x>>s[i].h;
    sort(s,s+n,cmp);
    for(int i=0;i<n;i++) cout<<s[i].t<<" "<<s[i].m<<" "<<s[i].x<<" "<<s[i].h<<endl;
    return 0;
}

第二种 重载 < 运算符(高手都是第二种,大家摇身变大佬,哈哈

#include<iostream>
#include<algorithm>
using namespace std;
struct ss{
   
    string t,m,x,h;
    int id;
    bool operator < (const ss &a)const{
     
        if(t.size()!=a.t.size()) return t.size()<a.t.size();
        if(t!=a.t) return t < a.t;
        return  id<a.id;
    }
}s[100005];
int n;
int main(){
   
    cin>>n;
    for(int i=0;i<n;i++) s[i].id=i,cin>>s[i].t>>s[i].m>>s[i].x>>s[i].h;
    sort(s,s+n);
    for(int i=0;i<n;i++) cout<<s[i].t<<" "<<s[i].m<<" "<<s[i].x<<" "<<s[i].h<<endl;
    return 0;
}

也可以用快排和归排,但是好麻烦,所有自己可以去试,只有把<重载了就行了
这里有一道要求排序题 - > 建议使用重载<去做排序


最后一道签到题
不讲了,这都讲,那太没无语了

#include<iostream>
 
using namespace std;
const int N=1e6+10;
int n,m=0x3f3f3f3f;
int main(){
   
    int t,x;
    cin>>n;
    for(int i=1;i<=n;i++){
   
        cin>>x;
        if(m>x)  m=x,t=i;
    }
    cout<<t<<endl;
    return 0;
}

感谢大家的观看,我花了好久打完了,希望能帮到大家
完结撒花
点点赞,辛苦死我了/(ㄒoㄒ)/~~


转载:https://blog.csdn.net/m0_52361859/article/details/113059653
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场