快乐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的,可是这属于全排列的范畴,也可以用STL里的全排列神器
next_permutation 不是很了解的 传送门 -> next_permutation详细用法
这道题如果使用神器的话就太简单了,把区间里的数全累加起来取模就行,判断次数
#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