大一上 对ACM的尝试与反思

/*虽然经过了一学期的努力(摸鱼)很遗憾地被OUT了,没有进入ACM集训队,但是这也给我了一次自我反思的机会。自己是否真的适合这类竞赛? 经过仔细地思考,自己还是觉得这类竞赛确实不适合自己,可能自己在数理方面的知识,还有在一些想法(训练不到位)上面确实不如别人,但是经过这一学期的尝试,自己也收获颇丰。至少也是一次成功的试错,接下来我要重新振作,去尝试更多的领域,希望能以此找到自己的用武之地。*/

目录:

一、对校赛的一点反思

二、准备ACM校赛时的一些学习心得(部分来自网络)

        1、对输入和输出的一些总结:

        2、对三种素数筛的总结:

        3、分解质因数:

        4、结构体:

        5、排序:

        6、前缀和以及差分:

        7、快速幂:

        8、查找

        9、搜索

       10、高精度

一、对校赛的一点反思:

        首先感谢韩教练和ACM集训队学长学姐们的辛勤付出,使得这次校赛能够成功举行。虽然这次比赛的成绩(高罚时解了5道题)对于我而言不是很理想,但是还是让我发现了自己存在的问题,比如自己在对几何有关的问题上以及一些与数学相关性很大的问题上的练习还是不够,还有在一些问题上的考虑不够,导致Wrong Answer,然后白白地增加罚时。我印象最深刻的就是那道“加上+或X然后求出最少要删去的数字个数”,思路是正确的,就是漏了两种情况的特判,所以导致了两次的92,第三次才顺利Accept,然后还有那道“Python是世界上最好的语言”,我Wrong Answer了四五次后才顺利解决,也是到后来才真正理解题意,然后重新用了一个数组来保存结果,顺利Accept。赛前去学习了一些算法,也整理了一堆板子,但是可能由于实力的欠缺,可能都没有做到要用那些算法的题目,做到的题目还是想法题居多。所以总结得出自己在这些想法题上的训练还不够,最明显的就是F题,那道稳定点的题目,我就是迟迟发现不了规律,到最后也是毫无想法,赛后和同学讨论,听说好像就是一个二维的题目,根本用不到三维。看到那么多同学都做出来了,而自己迟迟没有找到突破口,那时候心里确实不好受。无论如何,比赛也已经结束,感谢之前的热身赛和这次校赛,让我认清了自己的实力,发现了自己的不足,但同时也让我看到了自己的成长,自己从一个什么都不会的小白,一步一步地学会用代码去解决问题,想到这里,我觉得自己的努力也还是得到了回报。

二、准备ACM校赛时的一些学习心得

1、对输入和输出的一些总结:

#include<bits/stdc++.h>
char a[105];
int main(){
	int n;	
	memset(a,0,sizeof a);
	scanf("%d",&n);
	getchar();
    //scanf()会读取换行符!
	for(int i=0;i<4*n;i++)
	{
		scanf("%c",&a[i]);
	}
	for(int i=0;i<4*n;i++)
	{
		printf("%c",a[i]);
	}
	return 0;
} 
 //不同输入方式的对比
char c[10];
scanf("%s",c);//以空格结束
gets(c);//以回车结束

1.按%c格式符一个个单独输入输出

#include<bits/stdc++.h>
using namespace std;
int main()
{
    char a[100];
    for(int i=1;a[i]!='\0';i++)
    {
        //一般来说字符串大小未知,用‘/0’来判断是否结束
        scanf("%c",&a[i]);
    }
    for(int i=1;a[i]!='\0';i++)
    {
        printf("%c",a[i]);
    }
    return 0;
}

2 . 按%s格式符,将字符串作为一个整体输入输出

scanf("%s",a);
printf("%s"a);
//这里输入不能输入空格,空格与回车均表示输入结束。

3 . 用gets()输入。用puts()输出字符串

gets(a);//不需要写下标
puts(a);//自动输出一个换行。
//gets()以回车为终止符,可以读入空格。

4.用getchar(),putchar()输入和输出一个字符

ch=getchar();
putchar(ch);
/*编写程序,用getchar函数读入两个字符给c1、c2,
然后分别用putchar函数和printf函数输出这两个字符。
比较用printf和putchar函数输出字符的特点。*/
#include<stdio.h>
int main()
{
 char c1, c2;
 c1 = getchar();
 c2 = getchar();
 printf("用putchar输出结果为:");
 putchar(c1);
 putchar(c2);
 printf("\n");
 printf("用printf输出结果为:");
 printf("%c %c\n",c1,c2);
 return 0;
}

//输入时没有空格
ab
用putchar输出的结果为:ab
用printf输出的结果为:a b
    
//输入时有空格
a b
用putchar输出的结果为:a
用printf输出的结果为:a
 
//输入时有回车
a
用putchar输出的结果为:a
用printf输出的结果为:a  

5.用getline()输出

int n,cnt;
string s;
scanf("%d",&n);
getchar();
getline(cin,s);//整行输入字符串s 包括空格

6.cin.getline()

//此函数会一次读取多个字符(包括空白字符)
char a[100];
cin.getline(a,sizeof(a));

7.cin.get()

这个以后再补充吧。。。

2、对三种素数筛的总结:

暴力判断法:判断的数字较小,但是个数多;

筛法:需要判断的数字较大,但是需要判断的数字个数较少;

一、暴力枚举法 时间复杂度为 O(n*sqrt(n))

//这个是判断1~n那些是素数

int prime[200000]={0},c=0;  //prime[]存放素数的数组、c是数组里面素数的个数
void getprime(int n)
{
    prime[c++] = 2;    //prime[1]=2肯定是素数
    for(int i=3;i<=n;i+=2)   //素数肯定是奇数,所以i = i+2
    {
        int flag = 0;  //判断i是不是素数标记
        for(int j=2;j*j<=i;j++)   //判断i是不是还有其他因数,
        {
            if(i%j==0)  //如果i还有其他因数,则不是素数
            {
                flag = 1;  //标志i不是素数
                break;     //退出对i判断的循环
            }
        }
        //如果执行了上面代码flag还是0,说明i就是素数
        if(flag==0) prime[c++] = i;
    }
}

二、埃氏筛 时间复杂度为 O(n*log(log n))

bool isPrime[20000];
void getprime(int n)
{
    for(int i=2;i<=n;i++)
    {
     isPrime[i] = true;  //假设2~n都是素数
    }
    
    for(int i=2;i<=n;i++)
    {
        //遍历2~n里面所有数
        if(isPrime[i]==true)
        {
        //i是素数的话,那么i的倍数肯定就不是
        for(int j=i;i*j<=n;j++)
         {
           isPrime[i*j] = false;
         }
        //即 i*2,i*3 .....i*j肯定不是素数,注意边界i*j<=n
        //n以内,且是i的倍数的数肯定不是素数,设为false
        } 
    }   
    for(int i=2;i<=n;i++)
    {
	printf("%d ",isPrime[i]);
    }
}

三、欧拉筛 时间复杂度为 O(n)

//自己找的欧拉筛
#include<bits/stdc++.h>
using namespace std;
bool a[20000];
int prime[20000];
int main()
{	
    int n;
    int k=0;
    scanf("%d",&n);
	//n是范围,用k来给不断变大的质数数组计数 
	for(int i=2;i<=n;i++)
	{	//筛除2~n的合数
		 if(a[i]==0)
		 {
		 	prime[++k]=i;
             //prime[1]=2肯定是素数
		 	//不断地加入新的质数,循环判断 
		 }
		 for(int j=1;j<=k;j++)
		 {
		 	if(prime[j]*i>n)
		 	{
		 		break;
                //超过边界,不再筛
			}
			 a[prime[j]*i]=1;//素数乘积在不断变化
			 if(i%prime[j]==0)
			 {
			 	break;
                 //当乘数是被乘数的倍数时,停止筛 
			 }
		 }
	}
    for(int i=1;i<=k;i++)
    {
        printf("%d ",prime[i]);
    }
	return 0;
}

***简化版***

for(int i=2;i<=n;++i)
	{
		if(!vis[i])pri[++cnt]=i;
		for(int j=1;j<=cnt&&pri[j]*i<=n;++j)
		{
			vis[i*pri[j]]=1;
			if(i%pri[j]==0)break;
		}
	}

3、分解质因数:

#include<bits/stdc++.h>
using namespace std;
​
int Isprime(int n)
{
    for (int i=2; i*i<=n; i++)
    {
        if (n % i == 0) 
        {
            return 1;   
        }   
    }   
    return 0;
}
void fun(int n)
{
    int j,i=0;
    int m=n;
    for (j=2;j<=m/2;j++)
            while(n%j==0)
            {
                printf("%d ",j);
                n/=j;
            }
}
int main()
{
    int n;
    int c;
    scanf("%d", &n);
    c=Isprime(n);
    if(c)
    {
        fun(n);
    }
    else printf("it is a prime\n");
}
​

4、结构体:

一、定义

//1、先定义结构体类型,再定义结构体变量
struct   student 
{       
 int num;
 char  name[20];
 char sex;
 int age; 
 float score;
 char addr[30];
};
 struct student stu1,stu2;         
//***2、定义结构体类型的同时定义结构体变量
struct   data
 {      
  int day ;
  int month;
  int year;
 }time1,time2; 
//3、直接定义结构体变量
struct 
{
 char name[20];
 char sex; 
 int num; 
 float score[3]
 }person1,person2; 

二、结构体变量的初始化

#include <bits/stdc++.h>
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
int main()
{
    printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}

三、访问结构成员

为了访问结构的成员,我们使用成员访问运算符(.)

引用形式:<结构体类型变量名>.<成员名>

#include <bits/stdc++.h>
int main(){
    struct{
        char *name;  //姓名
        int num;  //学号
        int age;  //年龄
        char group;  //所在小组
        float score;  //成绩
    } stu1;
    //给结构体成员赋值
    stu1.name = "Tom";
    stu1.num = 12;
    stu1.age = 18;
    stu1.group = 'A';
    stu1.score = 136.5;
    //读取结构体成员的值
    printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", stu1.name, stu1.num, stu1.age, stu1.group, stu1.score);
    return 0;
}
//不过整体赋值仅限于定义结构体变量的时候,在使用过程中只能对成员逐一赋值,这和数组的赋值非常类似。
struct{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组
    float score;  //成绩
} stu1, stu2 = { "Tom", 12, 18, 'A', 136.5 };

四、结构作为函数的参数

#include <bits/stdc++.h>
struct Books
{
    char title[50];
    char author[50];
    char subject[100];
    int book_id;
};//先定义一个结构体变量
void printBook(struct Books book);//函数声明
int main()
{
    //声明Book1,类型为Books
    struct Books Book1;
    struct Books Book2;
    //Book1 详述
    strcpy(Book1.title, "C Programming");
    strcpy(Book1.author, "Nuha Ali");
    strcpy(Book1.subject, "C Programming Tutorial");
    //strcpy()把从src地址开始,且含有NULL结束符的字符串复制到以dest开始的地址空间
    //strcpy函数的原型是:
    //char * strcpy(char * strDest,const char * strSrc);
    Book1.book_id = 6495407;
    printBook(Book1);
    return 0;
}
void printBook(struct Books book)
{
    printf("Book title:%s\n", book.title);
    printf("Book author:%s\n", book.author);
    printf("Book subject:%s\n", book.subject);
    printf("Book book_id:%d\n", book.book_id);
}

五、结构体数组

//结构体数组在定义的同时也可以初始化
struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组 
    float score;  //成绩
}class[5] = {
    {"Li ping", 5, 18, 'C', 145.0},
    {"Zhang ping", 4, 19, 'A', 130.5},
    {"He fang", 1, 18, 'A', 148.5},
    {"Cheng ling", 2, 17, 'F', 139.0},
    {"Wang ming", 3, 17, 'B', 144.5}
};
//对数组中的全部元素赋值时,也可以不给出数组的长度
struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组 
    float score;  //成绩
}class[] = {
    {"Li ping", 5, 18, 'C', 145.0},
    {"Zhang ping", 4, 19, 'A', 130.5},
    {"He fang", 1, 18, 'A', 148.5},
    {"Cheng ling", 2, 17, 'F', 139.0},
    {"Wang ming", 3, 17, 'B', 144.5}
};

例:结构体数组的使用

#include <bits/stdc++.h>
struct{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组
    float score;  //成绩
}class[] = {
    {"Li ping", 5, 18, 'C', 145.0},
    {"Zhang ping", 4, 19, 'A', 130.5},
    {"He fang", 1, 18, 'A', 148.5},
    {"Cheng ling", 2, 17, 'F', 139.0},
    {"Wang ming", 3, 17, 'B', 144.5}
} ;
int main(){
    int i, num_140 = 0;
    float sum = 0;
    for(i=0; i<5; i++){
        sum += class[i].score;
        if(class[i].score < 140) 
        {
            num_140++;
        }
    }
    printf("sum=%.2f\naverage=%.2f\nnum_140=%d\n", sum, sum/5, num_140);
    return 0;
}

六.指向结构的指针

定义形式:struct 结构体名 *结构体指针名;

struct Books *struct_pointer;

定义之后可以在上述定义的指针变量中存储结构变量的地址

例:struct_pointer = &Book1;

为了使用指向该结构的指针访问结构的成员,必须使用->运算符

如下所示:struct_pointer->title;

//定义结构体指针的实例
struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组
    float score;  //成绩
} stu1 = { "Tom", 12, 18, 'A', 136.5 };
//结构体指针
struct stu *pstu = &stu1;
//可以在定义结构体的同时定义结构体指针
struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组
    float score;  //成绩
} stu1 = { "Tom", 12, 18, 'A', 136.5 }, *pstu = &stu1;

七.获取结构体成员

#include <bits/stdc++.h>
int main(){
    struct{
        char *name;  //姓名
        int num;  //学号
        int age;  //年龄
        char group;  //所在小组
        float score;  //成绩
    } stu1 = { "Tom", 12, 18, 'A', 136.5 }, *pstu = &stu1;
    //读取结构体成员的值
    printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", (*pstu).name, (*pstu).num, (*pstu).age, (*pstu).group, (*pstu).score);
    printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", pstu->name, pstu->num, pstu->age, pstu->group, pstu->score);
    //直接通过指针名加->访问
    return 0;
}

八、结构体数组指针的使用

#include <stdio.h>
struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组
    float score;  //成绩
}stus[] = {
    {"Zhou ping", 5, 18, 'C', 145.0},
    {"Zhang ping", 4, 19, 'A', 130.5},
    {"Liu fang", 1, 18, 'A', 148.5},
    {"Cheng ling", 2, 17, 'F', 139.0},
    {"Wang ming", 3, 17, 'B', 144.5}
}, *ps=stus;
int main(){
    //求数组长度
    int len = sizeof(stus) / sizeof(struct stu);
    printf("Name\t\tNum\tAge\tGroup\tScore\t\n");
    //\t是水平制表符
    for(ps=stus; ps<stus+len; ps++){
        printf("%s\t%d\t%d\t%c\t%.1f\n", ps->name, ps->num, ps->age, ps->group, ps->score);
    }
    return 0;
}

5、排序:

1、C++ sort函数的用法(快速排序) 时间复杂度为n*log2(n)

模板 Sort(start,end,排序方法)

默认的排序方法是从小到大排序

less<数据类型>()//从小到大排序

#include<bits/stdc++.h>
using namespace std;
int main()
{
 int a[10]={9,6,3,8,5,2,7,4,1,0}; 
sort(a,a+10,less<int>());
 for(int i=0;i<10;i++)
 cout<<a[i]<<endl;
 return 0;
}

greater<数据类型>()//从大到小排序

#include<bits/stdc++.h>
using namespace std;
int main()
{
 int a[10]={9,6,3,8,5,2,7,4,1,0}; 
 sort(a,a+10,greater<int>());
 for(int i=0;i<10;i++)
 {
 cout<<a[i]<<endl;
 }
 return 0;
}

对字符进行排序

#include<bits/stdc++.h>
using namespace std;
int main()
{
 char a[11]="asdfghjklk";
 sort(a,a+10,greater<char>());
 for(int i=0;i<10;i++)
 cout<<a[i]<<endl;
 return 0;
}

例1、使用默认排序方法(没有第三参数)

#include<bits/stdc++.h>
using namespace std;
int main()
{
 int a[10]={9,6,3,8,5,2,7,4,1,0};
 sort(a,a+10);
 for(int i=0;i<10;i++) 
 {
 cout<<a[i]<<endl;
 }
 return 0;
}

例2、从大到小排序 (加入自定义比较函数 cmp())

#include<bits/stdc++.h>
using namespace std;
bool cmp(int a,int b)
{
 return a>b;
}
int main() 
{
 int a[10]={9,6,3,8,5,2,7,4,1,0};
 sort(a,a+10,cmp);//在这里就不需要对cmp函数传入参数了,这是规则
 for(int i=0;i<10;i++)
 cout<<a[i]<<endl;
 return 0;
}

2、冒泡排序

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,a[100];
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<=n-1;i++)
    {
        for(int j=i+1;i<=n-1;j++)
        {
            if(a[j]>a[j+1]) //升序
            //相邻两个元素比较
            //要进行多次
            swap(a[j],a[j+1])
        }
    }
return 0;
}

3、选择排序

int a[100];//假装有数,并且有n个。
int k;
for(int i=0;i<=n-1;i++)
{
    k=i;
    for(int j=i+1;j<=n;j++)
    {
        if(a[i]<a[j])
        k=j;
    }
    if(k!=i)
    swap(a[i],a[k]);
}

4、插入排序

int a[100],n;   //n个数。
for(int out=1;out<n;out++)
{    
    int temp=a[out];
    int in=out;
    while(in>0&&temp<a[in-1])
    {
        a[in]=a[in-1];
        in--;
    }
    a[in]=temp;
}

5、桶排序 O(m*n)

int main(){

	const int m=11;

	const int n=20;

	int b[m];

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

	memset(b,0,sizeof(b));

	for(int i=0;i<n;++i)

	{

		b[a[i]]++;

	}

	for(int i=0;i<m;++i)

	{

		while(b[i]>0)

		{

			cout<<i<<" ";

			b[i]--;

		}

	}

	return 0;
}

6、前缀和以及差分:

一维前缀和

//代码1
#include<bits/stdc++.h>
using namespace std;
int main(){
    const int n=5;
    int a[n]={1,3,7,5,2};
    int sum[n];
    sum[0]=a[0];
    for(int i=1;i<n;i++)
    {
        sum[i]=sum[i-1]+a[i];   
    }
    //前缀和预处理
    for(int i=0;i<n;i++)
    {
        cout<<sum[i]<<" ";
    }
    return 0;   
} 


//代码2宏定义
#include<bits/stdc++.h>
using namespace std;
#define get_sum(L,R) (L!=0?sum[R]-sum[L-1]:sum[R])
int main(){
    const int n=5;
    int a[n]={1,3,7,5,2};
    int sum[n];
    sum[0]=a[0];
    for(int i=1;i<n;i++)
    {
        sum[i]=sum[i-1]+a[i];   
    }
        cout<<get_sum(2,4)<<" ";
        cout<<get_sum(0,3)<<" ";
        cout<<get_sum(3,4)<<" ";
    return 0;   
} 


//代码3自定义函数
#include<bits/stdc++.h>
using namespace std;
​
const int n=5;
int sum[n];
int get_sum(int L,int R)
{
    if(L!=0)
    {
        return sum[R]-sum[L-1];
    }else {
        return sum[R];
    }
}
int main(){
    int a[n]={1,3,7,5,2};
    sum[0]=a[0];
    for(int i=1;i<n;i++)
    {
        sum[i]=sum[i-1]+a[i];   
    }
        cout<<get_sum(2,4)<<" ";
        cout<<get_sum(0,3)<<" ";
        cout<<get_sum(3,4)<<" ";
    return 0;   
} 

二维前缀和

/*定义:sum[i][j]是求(0,0)到(i,j)的和
从大的求小的
sum[x1,y1][x2,y2]=sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]
从小的求大的
sum[i][j]=g[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]
*/
#include<bits/stdc++.h>
using namespace std;
const int n=3,m=4;
int sum[n][m];
int g[n][m]={
            {1,5,6,8},
            {9,6,7,3},
            {5,3,2,4}
            };
​
void pre_sum()
{
    sum[0][0]=g[0][0];
    for(int i=1;i<n;i++)
    {
        sum[i][0]=sum[i-1][0]+g[i][0];
        //第一列 
    }
    for(int j=1;j<m;j++)
    {
        sum[0][j]=sum[0][j-1]+g[0][j];
        //第一行 
    } 
    for(int i=1;i<n;i++)
    {
        for(int j=1;j<m;j++)
        {
            sum[i][j]=g[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
        }
    }
}
int get_sum(int x1,int y1,int x2,int y2)
{
    if(!x1 && !y1)
    {
        return sum[x2][y2];
    }
    
    if(!x1)
    {
        return sum[x2][y2]-sum[x2][y1-1];
    }
    
    if(!y1)
    {
        return sum[x2][y2]-sum[x1-1][y2];
    }
    //需要特判x1y1为0的情况 
    return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
}
int main()
{   
    pre_sum();
    cout<<get_sum(1,1,2,2)<<" "<<get_sum(0,1,1,3);
    return 0;
}

一维差分

int arr[10005];//定义一个差分数组 
int main() {
    int l,m;scanf("%d%d",&l,&m);
    for(int i=0;i<m;i++)
    {
        int u,v;scanf("%d%d",&u,&v);
        arr[u]+=1;
        arr[v+1]-=1;
    }
    int t=0,cnt=0;
    for(int i=0;i<=l;i++)
    {
        t+=arr[i];
        if(!t)cnt++;
    }
    printf("%d\n",cnt);
    return 0;
}


/*a 1 3 7 5 2
  d 1 2 4 -2 -3
  差分标记
  [l,r]+v==d[l]+v,d[r+1]-v
*/
#include<bits/stdc++.h>
using namespace std;
int d[6]={0};
void add(int l,int r,int v)
{
    d[l]+=v;
    d[r+1]-=v;
}
int main(){
    int a[5]={1,3,7,5,2};
    add(2,4,5);
    add(1,3,2);
    add(0,2,-3);
    for(int i=1;i<5;i++)
    {
        d[i]+=d[i-1];
        //这里就求和化为sum数组 
    }
    for(int i=0;i<5;i++)
    {
        a[i]+=d[i];
        cout<<a[i]<<" "; 
    }
    memset(d,0,sizeof(0));
    return 0;   
} 

二维差分

/*
d[x1][y1]+=v
d[x2+1][y2+1]+=v
d[x2+1][y1]-=v
d[x1][y2+1]-=v
*/
#include<bits/stdc++.h>
using namespace std;
​
const int n=3,m=4;
int sum[n][m];
int d[n+1][m+1];
int g[n][m]={
            {1,5,6,8},
            {9,6,7,3},
            {5,3,2,4}
            };
​
void pre_sum()
{
    sum[0][0]=d[0][0];
    for(int i=1;i<n;i++)
    {
        sum[i][0]=sum[i-1][0]+d[i][0];
        //第一列 
    }
    for(int j=1;j<m;j++)
    {
        sum[0][j]=sum[0][j-1]+d[0][j];
        //第一行 
    } 
    for(int i=1;i<n;i++)
    {
        for(int j=1;j<m;j++)
        {
            sum[i][j]=d[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
        }
    }
    for(int i=0;i<n;i++)
    {
    for(int j=0;j<m;j++)
    {
    g[i][j]+=sum[i][j]; 
    }   
    //把差分数组加回 
    } 
    
    memset(d,0,sizeof d);
    memset(sum,0,sizeof sum);
    
}
​
void print(){
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            cout<<g[i][j]<<" ";
        }
        cout<<endl;
    }
} 
void add(int x1,int x2,int y1,int y2,int v){
    d[x1][y1]+=v;
    d[x2+1][y1]-=v;
    d[x1][y2+1]-=v;
    d[x2+1][y2+1]+=v; 
}
int main()
{   
    add(0,0,2,1,3);
    add(1,1,2,2,-1); 
    pre_sum();
    print();
    return 0;
}

7、快速幂:

//简便求幂运算的方法
long long Power(long long base, long long power) {
    long long result = 1;
    while (power > 0)
    {
        if (power % 2 == 1)
        {	//指数为奇数时
            result = result * base % 1000;
            //先乘一次底数,指数化为偶数
            //底数也会不断地变化
        }
        power = power / 2;
        base = (base * base) % 1000;
        //最后一次的base运算对result无影响
    }
    return result;
}
//利用位运算的终极快速幂算法
long long Power(long long base, long long power) 
{
    long long result = 1;
    while (power > 0) 
    {
        if (power & 1) 
        {	//此处等价于if(power%2==1)
            result = result * base % 1000;
        }
        power >>= 1;
        //此处等价于power=power/2
        base = (base * base) % 1000;
    }
    return result;
}

8、查找

一、顺序查找

int k;//设k为待寻找的数值。
bool m=false;
for(int i=1;i<=n;i++)
{    
    if(a[i]==k)
    m=true;
}

二、二分查找

int zuo=1,you=s,mid;
int k;//待查找数值。
while(zuo<=you)
{
    mid=(zuo+you)/2;
    if(a[mid]==k)
    {
        cout<<"yes";
        return 0;
    }
    else if(a[mid]>k)
    you=mid-1;
    else if(a[mid]<k)
    zuo=mid+1;
}
cout<<"no";

三、binary_search 二分查找某个元素 是否出现

·函数模板:binary_search(arr[],arr[]+size , indx)

·参数说明:

        arr[]: 数组首地址

        size:数组元素个数

        indx:需要查找的值

·函数功能: 在数组中以二分法检索的方式查找,那复杂度为O(log n)

若在数组(要求数组元素非递减)中查找到indx元素则真,若查找不到则返回值为假。

#include <iostream>
#include <algorithm>
using namespace std;
int arr[500050];
int main()
{
    for(int i=0;i<10000;i++)
    {
        arr[i]=i;
    }
    cout << binary_search(arr,arr+10000,55) << endl;	//找得到55
    cout << binary_search(arr,arr+10000,-1) << endl;	//找不到-1
    return 0;
}

四、lower_bound()和upper_bound()在数组中的使用

upper_bound( a,a+n,x):从数组的begin位置到end-1位置二分查找第一个大于x的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

lower_bound( a,a+n,x):从数组的begin位置到end-1位置二分查找第一个大于或等于x的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

举个栗子:

在数组a[5]={0,2,3,4,5}中,对于需要查找的x=2

lower_bound(a,a+n,x)-a 返回值为1,表示数组中第一个大于等于x的下标值

upper_bound(a,a+n,x)-a 返回值为2,表示数组中第一个大于x的下标值

上面是对于非递减数列的用法,那对于非递增数列的用法呢? 这时候我们就需要传入第四个参数 greator () 其中TYPE是指数组类型 这时候我们lower_bound就是返回数组中第一个小于或等于x的数的位置,upper_bound就返回数组中第一个小于x的数的位置了。

#include<bits/stcd++.h>
using namespace std;
int seq1[] = {1, 2, 3, 3, 4, 5}, seq2[] = {9, 8, 7, 7, 6, 5};
int main()
{
    //cout<<upper_bound(seq1, seq1+6, 3, greater<int>()) - seq1<<endl;
    //cout<<lower_bound(seq1, seq1+6, 3, greater<int>()) - seq1<<endl;
    cout<<upper_bound(seq1, seq1+6, 3) - seq1<<endl;
    cout<<lower_bound(seq1, seq1+6, 3) - seq1<<endl;

    cout<<endl;

    cout<<upper_bound(seq2, seq2+6, 7, greater<int>()) - seq2<<endl;
    cout<<lower_bound(seq2, seq2+6, 7, greater<int>()) - seq2<<endl;
    //cout<<upper_bound(seq2, seq2+6, 7) - seq2<<endl;
    //cout<<lower_bound(seq2, seq2+6, 7) - seq2<<endl;
    return 0;
}

9、搜索

DFS(深度优先搜索)

迷宫问题

#include<bits/stdc++.h>
int m,n,p,q;
int min=99999999;
int a[100][100];
int v[100][100];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
//方向数组
//0表示未访问,1表示空地,2表示障碍物 
void dfs(int x,int y,int step){
	if(x==p&&y==q)
	{
		if(step<min)
		{
			min=step;
			return;
		}
	}
	for(int k=0;k<=3;k++)
	{
		int tx,ty;
		tx=x+dx[k];
		ty=y+dy[k];
		if(a[tx][ty]==1&&v[tx][ty]==0)
		{
			//a是地图数组
			//v是访问数组 
			v[tx][ty]=1;
			dfs(tx,ty,step+1);
			v[tx][ty]==0;
		}
	}
}
int main()
{	
	int startx,starty;
	scanf("%d%d",&m,&n);
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&a[i][j]);
		}
	}
	scanf("%d%d%d%d",&startx,&starty,&p,&q);
	v[startx][starty]==1;
	dfs(startx,starty,0);
	printf("%d",min);
	return 0;
	/*
	测试样例:
	5 4
	1 1 2 1
	1 1 1 1
	1 1 2 1
	1 2 1 1
	1 1 1 2
	
	1 1 4 3 
	*/
}

BFS(广度优先搜索)

#include<bits/stdc++.h>
using namespace std;
int a[100][100],v[100][100];
struct point{
	int x;
	int y;
	int step;
};
queue<point> r;//申请队列 
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int main()
{	
	int n,m,startx,starty,p,q;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&a[i][j]);
		}
	}
	scanf("%d%d%d%d",&startx,&starty,&p,&q);
	//BFS
	point start;
	start.x=startx;
	start.y=starty;
	start.step=0;
	r.push(start);//起点入队
	v[startx][starty]=1;
	int flag=0; 
	while(!r.empty())
	{
		int x=r.front().x,y=r.front().y;
		if(x==p&&y==q)
		{
			flag=1;
			printf("%d",r.front().step);
			break;
		}
		for(int k=0;k<=3;k++)
		{
			int tx,ty;
			tx=x+dx[k];
			ty=y+dy[k];
			if(a[tx][ty]==1&&v[tx][ty]==0)
			{
				point temp;
				temp.x=tx;
				temp.y=ty;
				temp.step=r.front().step+1;
				r.push(temp);
				v[tx][ty]=1;
			 } 
	 	}
		r.pop();//拓展完了就将队首出队 
}
if(flag==0)
{
	printf("no answer!\n");
}
/*
测试样例
5 4
1 1 2 1
1 1 1 1
1 1 2 1
1 2 1 1
1 1 1 2
1 1 4 3 
*/
	return 0; 
}

记忆化搜索

int f[9][9];
int dfs(int x,int y)
{
	if(f[x][y])return f[x][y];
    //对递归树进行剪枝,已访问过的就直接用
	if(x=n-1)f[x][y]=a[x][y];
	else{
		f[x][y]=a[x][y]+max(dfs(x+1,y),dfs(x+1,y+1));
		return f[x][y];
	}
}

最短路

一、Floyd算法(可以有负值)

int D[51][51];
int main(){
	int i,j,k,x,y;
	int N=5,M=6;//顶点数和边数
	int e[9][3]{{1,2,30},{1,3,10},
				{2,4,20},{2,5,25},
				{3,4,10},{4,5,10}
	};//初始化D 
	for(i=1;i<=N;++i)
	{
		for(j=1;j<=N;++j)
		{
			if(i!=j)D[i][j]=10000;
		}
	}
	for(i=1;i<=M;++i)
	{
		x=e[i-1][0];y=e[i-1][1];
		D[x][y]=D[y][x]=e[i-1][2];
	}
	printf("跟踪\n   i j k ik kj ij\n");
	for(i=1;i<=N/2+1;++i)
	{	//起点 
		for(j=1;j<=N;++j)
		{	//终点 
			for(k=1;k<=N;++k)
			{	//中间点 
				if(D[i][k]+D[k][j]<D[i][j])
				{
					printf("前 %d %d %d %d %d %d\n",i,j,k,D[i][k],D[k][j],D[i][j]);
					D[i][j]=D[i][k]+D[k][j];//更新最小值
					D[j][i]=D[i][j];
					printf("后 %d %d %d %d %d %d\n",i,j,k,D[i][k],D[k][j],D[i][j]); 
				}
			}			
		} 
	}
	printf("\nD[1][5]=%d\n",D[1][5]);
	return 0;
}

二、Dijkstra算法(不可以有负值)

int w[51][51];//边权值 
int d[51];//d[i]表示从源点s到i的距离 
int pre[51];//pre[i]表示i的前驱点 
bool b[51];//白点标记 

void print(int i)
{	//递归输出路径 
	if(pre[i]!=0)print(pre[i]);
	printf("%d ",i);
}
int main(){
	int i,j,k,minn,oo=10000,s=1;//起点 
	int n=5,m=7;//顶点数和边数 
	int e[9][3]{{1,2,2},{1,3,5},{1,4,2},
				{2,3,3},{3,4,1},{2,5,6},{3,5,2}};//边 			
	for(i=1;i<=n;++i)
	{	//储存边的权值 
		for(j=1;j<=n;++j)
		{
			w[i][j]=oo;
		}
	}
	for(k=1;k<=m;++k)
	{
		i=e[k-1][0];j=e[k-1][1];
		w[i][j]=w[j][i]=e[k-1][2];
	}
	for(i=1;i<=n;++i)
	{	//初始化d和pre 
		d[i]=oo;
		pre[i]=0;
	}
	d[s]=0;
	for(i=1;i<=n;++i)//求解最短距离和路径 
	{	//找到一个中转点,并洗白 
		minn=oo;k=0;
		for(j=1;j<=n;++j)
		{
			if(!b[j]&&d[j]<minn)
			{
				minn=d[j];k=j;
			}
		}
		b[k]=true;//洗白 
		//通过中转点,更新剩余蓝点到源点的距离 
		printf("%d:洗白%d\n",i,k);
		for(j=1;j<=n;++j)
		{
			if(d[k]+w[k][j]<d[j])
			{
				d[j]=d[k]+w[k][j];
				pre[j]=k;
				printf("  D[%d]=%d  Pre[%d]=%d\n",j,d[j],j,pre[j]);//跟踪 
			}
		}
	}
	//输出最短距离和路径 
	printf("\nD[%d]=%d\n",5,d[5]);
	print(5);
	return 0;
}

10、高精度

一、加法

int a[101],b[101],c[101];
int main(){
	int lena,lenb,lenc,i,j;
	string s1="10000000000000000000";
	string s2="200000000000000000000";
	lena=s1.length();
	lenb=s2.length();
	//倒序储存
	for(i=1;i<=lena;++i)
	{
		a[i]=s1[lena-i]-'0';
	} 
	for(i=1;i<=lenb;++i)
	{
		b[i]=s2[lenb-i]-'0';
	}
	lenc=max(lena,lenb);
	for(i=1;i<=lenc;++i)
	{	//第i位相加并加之前的进位 
		c[i]+=a[i]+b[i];
		c[i+1]=c[i]/10;//进位 
		c[i]%=10;//存余数 
	}
	if(c[lenc+1]>0)lenc++;//处理最高进位
	for(i=lenc;i>=1;--i)
	{
		cout<<c[i];
	} 
	cout<<endl;
	return 0;
}

二、减法

int a[256],b[256],c[256];
int main(){
	int lena,lenb,lenc,i;
	string s1="1314135132165132153132";
	string s2="793123135153131331348";
	lena=s1.length();
	lenb=s2.length();
	for(i=1;i<=lena;++i)
	{
		a[i]=s1[lena-i]-'0';
	} 
	for(i=1;i<=lenb;++i)
	{
		b[i]=s2[lenb-i]-'0';
	}
	for(i=1;i<=lena;++i)
	{	
		if(a[i]<b[i])
		{
			a[i+1]--;
			a[i]+=10;//借位相减	
		} 
		c[i]=a[i]-b[i];
	}
	lenc=lena;
	while(c[lenc]==0&&lenc>1)
	{
		lenc--;//删除前导0 
	}
	for(i=lenc;i>=1;i--)
	{
		cout<<c[i];//输出结果 
	}
	cout<<endl;
	return 0;
}

三、乘法

int a[256],b[256],c[256];
int main(){
	int lena,lenb,lenc,i,j;
	string s1="3135468956465321";
	string s2="1230000000000000000000";
	lena=s1.length();
	lenb=s2.length();
	for(i=1;i<=lena;++i)
	{
		a[i]=s1[lena-i]-'0';
	} 
	for(i=1;i<=lenb;++i)
	{
		b[i]=s2[lenb-i]-'0';
	}
	
	for(i=1;i<=lenb;++i)
	{	//乘数循环 
		for(j=1;j<=lena;++j)
		{	//被乘数循环 
			c[i+j-1]+=a[j]*b[i];//累加乘积 
		}
	}
	lenc=lena+lenb-1;
	for(i=1;i<=lenc;++i)
	{
		c[i+1]+=c[i]/10;//累加进位 
		c[i]%=10;//存余数 
	}
	lenc++;
	while(c[lenc]==0&&lenc>1)
	{
		lenc--;//删除前导0 
	}
	for(i=lenc;i>=1;i--)
	{
		cout<<c[i];
	}
	cout<<endl;
	return 0;
}

四、除法

//1.高精除以低精
#include <bits/stdc++.h>
#define cintie std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define Show(x) std::cout << #x <<" = "<< x << std::endl
#define showld(x) printf("n=%lld",n);
#define showf(x) printf("n=%lf",n);
using namespace std;
typedef long long ll;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

int a[256],c[256];
int main(){
	int lena,lenc,b,i,x;
	string s="2986";
	b=52;
	lena=s.length();
	//顺序储存 
	for(i=1;i<=lena;++i)
	{
		a[i]=s[i-1]-'0';
	} 
	x=0;
	for(i=1;i<=lena;++i)
	{	//按位相除 
		c[i]=(x*10+a[i])/b;//每次的商
		x=(x*10+a[i])%b;//每次的余数 
	}
	lenc=1;
	while(c[lenc]==0&&lenc<lena) 
	{
		lenc++;
	}
	for(i=lenc;i<=lena;i++)
	{
		cout<<c[i];
	}
	cout<<" "<<x<<endl;//余数 
	return 0;
}
//2.高精除以高精——用减法来模拟减法
#include <bits/stdc++.h>
#define cintie std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define Show(x) std::cout << #x <<" = "<< x << std::endl
#define showld(x) printf("n=%lld",n);
#define showf(x) printf("n=%lf",n);
using namespace std;
typedef long long ll;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

int compare(int a[],int b[])
{	//比较数组 
	if(a[0]>b[0])return 1;
	//a的位数更多 
	if(a[0]<b[0])return -1;
	//b的位数更多 
	for(int i=a[0];i>0;--i)
	{	//从高位到低位比较 
		if(a[i]>b[i])return 1;
		if(a[i]<b[i])return -1;
	}
	return 0;//各位都相等则两数相等 
} 

void copyarray(int p[],int q[],int m)
{	//复制数组:复制p数组到q数组从m开始 
    //构造每次的减数
	for(int i=1;i<=p[0];++i)
	{
		q[i+m-1]=p[i]; 
	}
		q[0]=p[0]+m-1;
}

void subtraction(int a[],int b[])
{	//数组求差:a=a-b 
	int flag,i;
	flag=compare(a,b);//调用比较函数 
	if(flag==0){a[0]=0;return ;}//相等 
	if(flag==1)//大于 
	{
		for(i=1;i<=a[0];++i)
		{
			if(a[i]<b[i])//若不够减 
			{
				a[i+1]--;//向上借一位 
				a[i]+=10;
			}
			a[i]-=b[i];//结果存入a 
		}
		while(a[0]>0&&a[a[0]]==0)a[0]--;
	}
}

void division(int a[],int b[],int c[])
{	//用减法来模拟除法 
	int i,tmp[101];
	c[0]=a[0]-b[0]+1;
	for(i=c[0];i>=1;--i)
	{
		memset(tmp,0,sizeof(tmp));//清零 
		copyarray(b,tmp,i);//tmp做减数 
		while(compare(a,tmp)>=0)
		{
			c[i]++;//减法依次计数 
			subtraction(a,tmp);//数组求差 
		}
	}
	while(c[0]>0&&c[c[0]]==0)c[0]--;
}

void print(int a[])
{	//打印输出 
	if(a[0]==0){cout<<0<<endl;return ;} 
	for(int i=a[0];i>0;--i)
	{
		cout<<a[i];
	}
	cout<<endl;
}

int a[256],b[256],c[256];
int main(){
	int i;
	string s1="123456453216151351551351000000";
	string s2="51315315351531";
	a[0]=s1.length();//存被除数的位数 
	b[0]=s2.length();//存除数的位数 
	for(i=1;i<=a[0];++i)
	{
		a[i]=s1[a[0]-i]-'0';
	}	
	for(i=1;i<=b[0];++i)
	{
		b[i]=s2[b[0]-i]-'0';
	}
	division(a,b,c);//模拟除法 
	print(c);print(a);//打印 
	return 0;
}

/*茫茫人海,感谢你能看到这里*/

//printf("\\(X_X)/ Says : \"I can 100%% print this !\"");

Accept Accept Accept

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

goodlunatic

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值