day01:三大结构&数组&数组排序&查找(顺序查找&二分查找)

day01:三大结构&数组&数组排序&查找(顺序查找&二分查找)

三大结构:
顺序结构:从上至下,从左至右的执行
分支结构:满足flag为真就执行

if(flag){}

if(flag){}
else{}

if(flag){}
else if(flag){}
else if(flag){}
else{}

循环结构:满足flag为真就一直执行

for(int i=0; i<n; i++){}
while(flag){}
do{}while(flag);

数组
数组是同类数据的一个集合,开辟的是一段连续的内存空间,
二维数组的下一行接着上一行的末尾。

初始化方式

int a[10];
int a[10]={0,1,2,3,4,5,6,7,8,9};
int a[10]={0,1,2,3};

const int N=10;
int a[N];

int类型,全局变量默认初始化为0,
局部变量如果未初始化,则初始化为随机数,
局部变量如果部分初始化,则未初始化部分为0,

数组排序:

sort(a,a+n);//#include<algorithm>

sort(a, a+n); //sort(首地址,首地址+元素个数)

sort(a, a+n, cmp);//自定义比较函数,修改排序规则

sort() 默认升序排列,内部封装快速排序方法,时间复杂度 nlogn 

选择排序
每一次选择最小的元素放在未排序的最前面,时间复杂度:O(n^2)

for(int i=0; i<n-1; i++){//选择排序
    for(int j=i+1; j<n; j++){
        if(a[i]>a[j]) {
            swap(a[i], a[j]);//交换 a[i],a[j]
        }
    }
}

归并排序(暂作了解):分治&归并(作为最后的一个补充)

#include<bits/stdc++.h>
using namespace std;
/*
归并排序:分治思想,先分小,再治小(排序),最后合并
在所有排序中,归并排序最为稳定,时间复杂为 logn,也就是log2(n)
*/
int a[10]= {3,4,5,6,2,1,6,7,8,9};
int n=sizeof(a)/sizeof(int);   //通过数组大小计算元素个数
void merge(int arr[], int l, int mid, int r) {
    int i=l, j=mid+1, t=0, temp[r];//开一个临时数组,存放答案
    while(i<=mid && j<=r) {
        if(arr[i]<=arr[j]) temp[t++] = arr[i++];
        else temp[t++] = arr[j++];
    }
    while(i<=mid)  temp[t++] = arr[i++];//处理剩余元素
    while(j<=r)    temp[t++] = arr[j++];
    t = 0;
    while(l<=r)	arr[l++] = temp[t++];//将答案赋给原数组
}
//int *arr 在这里等同于 int a[],不过是使用指针的格式进行传参
void MergeSort(int * arr,int l, int r) {
    if(l<r) {
        int mid = (l+r)/2;    //取中间作为分割点
        MergeSort(arr, l, mid);    //分左边
        MergeSort(arr, mid+1, r);  //分右边
        merge(arr, l, mid, r);     //左右合并
    }
}
int main() {
    MergeSort(a, 0, n-1);//自定义排序函数,MergeSort(数组首地址,排序起始下标,排序结束下标);
    for(int i=0; i<n; i++) {
        cout<<a[i]<<" ";
    }
    return 0;
}

数组是一段连续的内存空间,下标可以超过内存空间,且不会报错,但是结果不对

二维数组的下一行与上一行的末尾是相邻的

数组下标的灵活使用:int a[4][5];

00 01 02 03 04
10 11 12 13 14
20 21 22 23 24
30 31 32 33 34

ij: i表示行,j表示列,同行i不变,同列j不变 
左上角到右下角对角线:j-i是定值
左下角到右上角对角线:i+j是定值 

查找(普通查找&二分查找)
普通查找其实就是暴力枚举,直到找到目标
如:给你n个数据,存放在数组a[n]中,保证每个数不同,请找出某个数在该数组的位置

for(int i=0; i<n; i++){
    if(a[i]==x){
        //i即为所求
    }
}

二分查找是基于一个已经排好序的数列,在其中查找某个值
如:给你一个数列:int a[10]={0,1,2,3,4,5,6,7,8,9}; 快速查找x在数列中的位置

int a[10]= {0,1,2,3,4,5,6,7,8,9};
int l=0, r=sizeof(a)/sizeof(int)-1;
int x=3; //cin>>x;
while(l<=r){    //满足左下标小于等于右下标的前提 
    int mid = (l+r)/2;
    if(a[mid]<x){//中间的数比x小,则x在右半部分 
        l = mid+1; 
    }else if(a[mid]>x){//中间的数比x大,则x在左半部分
        r=mid-1;
    } else{
        cout<<mid; break;//找到答案,输出并退出循环 
    }
}

1.P3954 [NOIP2017 普及组] 成绩

【题目描述】牛牛最近学习了C++入门课程,这门课程的总成绩计算方法是:

总成绩=作业成绩×20%+小测成绩×30%+期末考试成绩×50%

牛牛想知道,这门课程自己最终能得到多少分。

输入格式:
三个非负整数A,B,C,分别表示牛牛的作业成绩、小测成绩和期末考试成绩。
相邻两个数之间用一个空格隔开,三项成绩满分都是100分。

输出格式:一个整数,即牛牛这门课程的总成绩,满分也是100分。

输入样例:100 100 80

输出样例:90

题解:这就是一个顺序结构,但是注意涉及到小数的要不要用double

#include<bits/stdc++.h>//万能头文件
using namespace std;
int main() {
    int a,b,c;    cin>>a>>b>>c;
    cout<<a*0.2+b*0.3+c*0.5;
    return 0;
}

2. P1085 [NOIP2004 普及组] 不高兴的津津

【题目描述】津津上初中了。妈妈认为津津应该更加用功学习,所以津津除了上学之外,还要参加妈妈为她报名的各科复习班。另外每周妈妈还会送她去学习朗诵、舞蹈和钢琴。但是津津如果一天上课超过八个小时就会不高兴,而且上得越久就会越不高兴。假设津津不会因为其它事不高兴,并且她的不高兴不会持续到第二天。请你帮忙检查一下津津下周的日程安排,看看下周她会不会不高兴;如果会的话,哪天最不高兴。

输入格式:
输入包括7行数据,分别表示周一到周日的日程安排。
每行包括两个小于10的非负整数,用空格隔开,
分别表示津津在学校上课的时间和妈妈安排她上课的时间。

输出格式:一个数字。
如果不会不高兴则输出0,如果会则输出最不高兴的是周几(用
1, 2, 3, 4, 5, 6, 7分别表示周一,周二,周三,周四,周五,周六,周日)。
如果有两天或两天以上不高兴的程度相当,则输出时间最靠前的一天。

输入样例:

5 3
6 2
7 2
5 3
5 4
0 4
0 6

输出样例:3

题解:就是求那天的和值最大,最大第一次出现的位置

#include<bits/stdc++.h>
using namespace std;
int main() {
    int max=0;    //最多上课时间
    int k=0;      //最不高兴的那一天
    for(int i=1; i<=7; i++) {
        int a,b;    cin>>a>>b;
        if(a+b>max && a+b>8) {
            max = a+b;    k = i;
        }
    }
    cout<<k;    return 0;
}

3. P1046 [NOIP2005 普及组] 陶陶摘苹果

【题目描述】陶陶家的院子里有一棵苹果树,每到秋天树上就会结出 10 个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。陶陶有个 30 厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。

现在已知 10 个苹果到地面的高度,以及陶陶把手伸直的时候能够达到的最大高度,请帮陶陶算一下她能够摘到的苹果的数目。假设她碰到苹果,苹果就会掉下来。

输入格式:输入包括两行数据((全部数据以厘米为单位))
第一行包含10个100到200之间(包括100和200)的整数分别表示10个苹果到地面的高度,两个相邻的整数之间用一个空格隔开。
第二行只包括一个100到120之间(包含100和120)的整数,表示陶陶把手伸直的时候能够达到的最大高度。

输出格式:输出一个整数,表示陶陶能够摘到的苹果的数目。

输入样例:

100 200 150 140 129 134 167 198 200 111
110

输出样例:5

题解:注意只要碰到就行,所以苹果高度<= 能摸到的最大高度 即可

#include<bits/stdc++.h>
using namespace std;
int main(){
    int a[10];
    for(int i=0; i<10; i++) cin>>a[i];
    int h, ans=0; cin>>h;
    for(int i=0; i<10; i++){
        if(a[i]<=h+30){//注意要等于
            ans++;
        }
    }
    cout<<ans; return 0;
}

4. P1980 [NOIP2013 普及组] 计数问题

【题目描述】试计算在区间 1 到 n 的所有整数中,数字x(0 ≤ x ≤ 9)共出现了多少次?

例如,在 1到11中,即在1,2,3,4,5,6,7,8,9,10,11 中,数字 11 出现了 44 次。

输入格式:2个整数n,x,之间用一个空格隔开。

输出格式:1个整数,表示x出现的次数。

输入样例:11 1

输出样例:4

题解:对1到n,进行一次枚举,同时判断枚举数据中的x个数

#include<bits/stdc++.h>
using namespace std;
int main(){ //n=1e6 = 1000000
    int n,x, ans=0; cin>>n>>x;
    for(int i=1; i<=n; i++){
        int temp=i; // 临时变量
        while(temp!=0){
            if(temp%10==x) ans++; //如果个位数等于x,则答案+1
            temp /= 10;// temp = temp/10; 舍去个位数
        }
    }
    cout<<ans;    return 0;
} 

5. P1035 [NOIP2002 普及组] 级数求和

【题目描述】已知:Sn= 1+1/2+1/3+…+1/n。显然对于任意一个整数 k,当 n 足够大的时候,Sn>k。

现给出一个整数 k,要求计算出一个最小的 n,使得 Sn>k。

输入格式:一个正整数 k

输出格式:一个正整数 n

输入样例:1

输出样例:2

题解:注意大坑:需要开double(小数后15位有效),float(小数后7位有效)会出bug。

#include<bits/stdc++.h>
using namespace std;
int main(){
    int k,n=0; cin>>k;
    double s=0;//注意double
    while(s<=k){
        ++n;    s += 1.0/n;//公式
    }
    cout<<n;    return 0;
}

6.P1008 [NOIP1998 普及组] 三连击

【题目描述】将1,2,…,9 共 9 个数分成 3 组,分别组成 3个三位数,且使这3个三位数构成 1 : 2 : 3的比例,试求出所有满足条件的3 个三位数。

输入格式:无输入

输出格式:若干行,每行 3 个数字。按照每行第1 个数字升序排列。

输入样例:无

输出样例:

192 384 576
* * *
...

* * *(剩余部分不予展示)

题解:注意使用技巧 -- 打标记,如果数字 i 出现了,那么 t[i]=1,否则 t[i] =0;

#include<bits/stdc++.h>
using namespace std;
int main(){
    for(int i=100; i<=333; i++){
        int a=i, b=2*i, c=3*i;
        int t[10]={0}, sum=0;//对1-9进行计数,出现过则为1,未出现则为0
        //memset(t, 0, sizeof(t)); //初始化为0
        t[a/100]=1, t[a/10%10]=1, t[a%10]=1; 
        t[b/100]=1, t[b/10%10]=1, t[b%10]=1; 
        t[c/100]=1, t[c/10%10]=1, t[c%10]=1; 
        for(int j=1; j<=9; j++) sum += t[j];
        if(sum==9) cout<<a<<" "<<b<<" "<<c<<endl;
    } return 0;
}

7. P1047 [NOIP2005 普及组] 校门外的树

【题目描述】某校大门外长度为 l 的马路上有一排树,每两棵相邻的树之间的间隔都是 1 米。我们可以把马路看成一个数轴,马路的一端在数轴 0 的位置,另一端在 l 的位置;数轴上的每个整数点,即 0,1,2,3,..., l 都种有一棵树。

由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。

输入格式:第一行有两个整数,分别表示马路的长度 l 和区域的数目 m。

接下来 m 行,每行两个整数 u, v,表示一个区域的起始点和终止点的坐标。

输出格式:输出一行一个整数,表示将这些树都移走后,马路上剩余的树木数量。

输入样例:

500 3
150 300
100 200
470 471

输出样例:298

题解:也是一个打标记的小技巧

#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
int a[N];//0表示有树,1表示无树
int main(){
    int l,m; cin>>l>>m;
    for(int i=1; i<=m; i++){
        int u,v; cin>>u>>v;
        for(int j=u; j<=v; j++) a[j]=1;//树被移栽了
    }
    int ans=0;
    for(int i=0; i<=l; i++)
        if(a[i]==0) ans++;//树木个数+1
    cout<<ans;    return 0;
}

8. P1059 [NOIP2006 普及组] 明明的随机数

【题目描述】明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。

输入格式:

输入有两行,第1行为1个正整数,表示所生成的随机数的个数N
第2行有N个用空格隔开的正整数,为所产生的随机数。

输出格式:

输出也是两行,第1行为1个正整数M,表示不相同的随机数的个数。
第2行为M个用空格隔开的正整数,为从小到大排好序的不相同的随机数。

输入样例:

10
20 40 32 67 40 20 89 300 400 15

输出样例:

8
15 20 32 40 67 89 300 400

题解:

#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
int a[N], b[N], cnt=0;
int main(){//解法1 - 桶排序的思想
    int n; cin>>n;
    for(int i=1; i<=n; i++){
        int x; cin>>x;
        a[x]=1;
    }
    for(int i=1; i<=1000; i++){
        if(a[i]==1) b[++cnt]=i;
    }
    cout<<cnt<<endl;
    for(int i=1; i<=cnt; i++)     cout<<b[i]<<" ";
    return 0;
}

int main_ac(){//解法2 - 模拟一遍,先排序,再去重
    int n; cin>>n;
    for(int i=0; i<n; i++) cin>>a[i];
    sort(a, a+n);//升序排序
    b[0]=a[0];
    for(int i=0; i<n-1; i++){
        if(b[cnt]!=a[i+1]){
            b[++cnt] = a[i+1];
        }
    }
    cout<<cnt+1<<endl;
    for(int i=0; i<=cnt; i++)    cout<<b[i]<<' ';
    return 0;
}

9.P2669 [NOIP2015 普及组] 金币

【题目描述】国王将金币作为工资,发放给忠诚的骑士。第一天,骑士收到一枚金币;之后两天(第二天和第三天),每天收到两枚金币;之后三天(第四、五、六天),每天收到三枚金币;之后四天(第七、八、九、十天),每天收到四枚金币……;这种工资发放模式会一直这样延续下去:当连续 n天每天收到 n枚金币后,骑士会在之后的连续 n+1天里,每天收到 n+1 枚金币。

请计算在前 k天里,骑士一共获得了多少金币。

输入格式:一个正整数 k,表示发放金币的天数。

输出格式:一个正整数,即骑士收到的金币数。

输入样例:6

输出样例:14

题解:主要是将题目信息归纳出来,学会如何归纳总结,养成用数据表达题意的习惯。

/* 根据题目可列出下表,找到规律
1
2 2
3 3 3
4 4 4 4
....*/
#include<bits/stdc++.h>
using namespace std;
int main(){
    int k,sum=0; cin>>k;
    int i=1;
    while(k>0){
        for(int j=1; j<=i && k>0; j++){
            sum += i;    k--;
        }
        i++;
    }
    cout<<sum;    return 0;
}

推荐:P1014 [NOIP1999 普及组] Cantor 表 这道题目也是同样类型的题目,只是分了左右两个方向

10.P1909 [NOIP2016 普及组] 买铅笔

【题目描述】P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物。她发现商店一共有 33种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起 见,P老师决定只买同一种包装的铅笔。

商店不允许将铅笔的包装拆开,因此P老师可能需要购买超过n支铅笔才够给小朋友们发礼物。现在P老师想知道,在商店每种包装的数量都足够的情况下,要买够至少n支铅笔最少需要花费多少钱。

输入格式:
第一行包含一个正整数n,表示需要的铅笔数量。
接下来三行,每行用2个正整数描述一种包装的铅笔:
其中第1个整数表示这种包装内铅笔的数量,第2个整数表示这种包装的价格。
保证所有的7个数都是不超过10000的正整数。

输出格式:1个整数,表示P老师最少需要花费的钱。

输入样例:

57
2 2
50 30
30 27

输出样例:54

题解:题目要求用最少的钱达到目的即可,所以先设money极大,依次取当前方案的最小值即可。

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n, money=1e8; cin>>n;
    for(int i=1; i<=3; i++){
        int num, price; cin>>num>>price;
        if(money>ceil(1.0*n/num)*price)//最便宜
            money=ceil(1.0*n/num)*price;
    }
    cout<<money;    return 0;
}

11. P1014 [NOIP1999 普及组] Cantor 表

【题目描述】

现代数学的著名证明之一是 Georg Cantor 证明了有理数是可枚举的。他是用下面这一张表来证明这一命题的:

1/1, 1/2, 1/3, 1/4, 1/5, …
2/1, 2/2, 2/3, 2/4, …
3/1, 3/2, 3/3, …
4/1, 4/2, …
5/1, …
…

我们以 Z 字形给上表的每一项编号。第一项是 1/1,然后是1/2,2/1,3/1,2/2,…

输入格式:整数N(1≤N≤107)。

输出格式:表中的第 N项。

输入样例:7

输出样例:1/4

题解:本题属于找规律的题目,要学会用数据来展示规律

#include<bits/stdc++.h>
using namespace std;
/* 找规律:
1/1
2/1    1/2
3/1    2/2    1/3
4/1    3/2    2/3    1/4
5/1    4/2    3/3    2/4    1/5
确定第n个数所在行列:i,j 
奇数行:左往右数
偶数行:右往左数
并且:x+y=i+1     */
int main() {
    int i=0,j=0,sum=0,x=1,y=1;
    int n;    cin>>n;
    while(sum<n){
        i++;
        sum += i;
    }
    j = sum-n+1;
    if(i%2!=0)  x=j, y=i+1-x;//奇数,左往右数
    else  x=i-j+1, y=i+1-x; //偶数,右往左数
    cout<<x<<"/"<<y<<endl;    return 0;
}
posted @ 2021-08-01 07:14  HelloHeBin  阅读(280)  评论(0编辑  收藏  举报