[C++] 刷题日记

C++:std::greater()、std::less()、自定义比较函数的规则 7.30

1.排序和建堆的效果
排序:
less<T>变成升序(从左到右遍历下标时,数组元素是从小到大)
greater<T>变成降序(从左到右遍历下标时,数组元素是从大到小)
建堆:
less<T>变成大顶堆(从上层到下层,堆元素是从大到小,同层之间随便)
greater<T>变成小顶堆(从上层到下层,堆元素是从小到大,同层之间随便)
lessgreater并不是直接对应汉语意思,不能统一。其实是真正的意思是两个要比较的元素,第一个元素是否比第二个元素更小less还是更大greater。

在数组中,less里的比较函数comp(前一个,后一个)比更less,因此是升序

在建堆中,less的比较函数comp(插入节点,与父节点)更less,因此是大根堆。

sort(a, a + 10, less<int>());        //升序
sort(a, a + 10, greater<int>());    //降序

 cin和scanf读取问题 date 7.23

首先scanf("%c",&c)//这样能读到空格、回车、Tab等。

scanf("%c",&c1);
scanf("%c",&c2);

例如:我输入

    char c1,c2;
    scanf("%c",&c1);
    scanf("%c",&c2);
    cout << "-->" <<c1<<"<--"<<endl<<"-->"<<c2<<"<--"<< std::endl;

输入TAB然后回车

 然而如果你读取的方式是这样:

    char c1,c2;
    scanf("%c %c",&c1,&c2);
    cout << "-->" <<c1<<"<--"<<endl<<"-->"<<c2<<"<--"<< std::endl;

scanf函数中,空格在格式字符串中的作用是用于指定输入的字符之间的分隔符。具体来说,在使用%c格式说明符读取字符时,scanf会跳过输入中的空白字符(空格、制表符、换行符等),直到遇到非空白字符为止。然后,它将非空白字符读取到相应的变量中。

如下:

题目中可能会出现如下的情况:

先读一个int类型,然后再读字符

    int i;
    char c1,c2;
    scanf("%d",&i);
    scanf("%c",&c2);
    cout << "-->" <<i<<"<--"<<endl<<"-->"<<c2<<"<--"<< std::endl;

如果你是这样读的,那么输入完第一个int后,在输入的空白字符(空格、制表符、换行符等)就会被直接存储到c2当中,如下:

输入1空格,然后回车 

输入1回车

 输入1空格2,然后回车

 想必应该明白了,因此有两种方法去弥补:

1.getchar()吃掉空白字符

    int i;
    char c1,c2;
    scanf("%d",&i);
    getchar();
    scanf("%c",&c2);
    cout << "-->" <<i<<"<--"<<endl<<"-->"<<c2<<"<--"<< std::endl;

 2.两次输入中添加分隔符

    int i;
    char c1,c2;
    scanf("%d %c",&i,&c2);
    cout << "-->" <<i<<"<--"<<endl<<"-->"<<c2<<"<--"<< std::endl;

cin读取字符串会在遇到空格、制表符或换行符时停止读取,因此如果您输入带有空格的字符串,它只会读取第一个单词。

    int i;
    char c1,c2;
    scanf("%c",&c1);
    string str;
    cin>>str;
    cout << "-->" <<c1<<"<--"<<endl<<"-->"<<str<<"<--"<< std::endl;

scanf读取字符串的时候则

	string a;
	a.resize(2); //需要预先分配空间
	scanf("%s", &a[0]);

字符数组/字符串作为函数参数 date 7.23

在C中,字符数组通常以指针的形式传递给函数。这是因为在C中,数组传递给函数时会自动退化为指针,所以函数形参中接收的是指向字符的指针。在这种情况下,函数内部可以通过指针修改数组的内容。

// 函数接受一个字符数组,并修改其内容
void modifyArray(char arr[]) {
    arr[0] = 'X'; // 修改数组的第一个元素为 'X'
}

当然若不希望修改字符数组的内容,则需要使用const修饰指针类型

// 函数接受一个 const char*(字符数组的指针),但不会修改其中的内容
void processArray(const char* arr) {
    // 试图修改 arr[0] = 'X'; 会导致编译错误
    printf("在函数中处理数组,arr[0] = %c\n", arr[0]);
}

也可将原始数组进行复制到局部数组当中:

// 函数接受一个字符数组,并不修改原始数组内容,而是处理副本
void processArray(const char arr[]) {
    // 在函数内部创建一个局部字符数组
    char localArray[10];
    strcpy(localArray, arr);

    // 对局部数组进行处理
    localArray[0] = 'X';

    printf("在函数中处理数组,localArray = %s\n", localArray);
}

在C++中,使用 std::string 类型可以更方便地处理字符串,而不需要显式传递指针。std::string 作为函数参数时,传递的是字符串的副本,函数对副本的修改不会影响原始字符串。

// 函数接受一个 std::string,并修改其内容
void modifyString(std::string str) {
    str[0] = 'X'; // 修改字符串的第一个字符为 'X'
}

如果想在函数内改变原始的std::string,可以通过传递std::string的引用作为函数参数。通过引用传递,函数可以直接修改原始std::string的内容,而不是操作副本。

// 函数接受一个 std::string 引用,并修改其内容
void modifyString(std::string& str) {
    str[0] = 'X'; // 修改字符串的第一个字符为 'X'
}

785. 快速排序 date 6-27

#include <iostream>

using namespace std;
const int N =1e6+10;

int q[N];
void quick_sort(int q[],int left,int right){
    if (left>=right) return ;

    //为了便于使用 do while
    int l = left-1;
    int r = right+1;


    //1.选取分界点
    int pivot = q[(l+r)>>1];//这个分界点是定值,第一次写的时候我写的是数组中间的那个值,但随着排序的过程中,中间的值会变化
    while (l<r){

        //2.划分,将数组按照分界点划分为小于分界点的部分和大于分界点的部分
        do l++;while(q[l]<pivot);
        do r--;while(q[r]>pivot);
        //2-1 此时指针都指向了不满足条件的值,则需要对二者进行交换
        if (l<r)swap(q[l],q[r]);
    }
    //3.递归调用左右两段(即小于等于临界点的部分和大于等于零界点的部分)
    quick_sort(q,left,r);
    quick_sort(q,r+1,right);
    //此时应该想起递归会递归到最简单的情景,
    //那么在函数的开始补上这个情景



}
int main(){
    int n;
    // scanf函数来读取用户输入的整数值
    //格式字符串%d表示读取一个整数值
    //使用&符号来获取数据元素的地址
    scanf("%d",&n);
    for (int i=0;i<n;i++)scanf("%d",&q[i]);
    //调用quick_sort函数,数组作为参数传递给函数时,
    //会通过指针或者引用的方式传递,而不是创建数组的副本
    quick_sort(q,0,n-1);
    for (int i=0;i<n;i++)printf("%d ",q[i]);
    return 0;
}

递归的两个条件:

  • 可以通过递归调用缩小问题的规模,且新问题与原问题有着相同的形式(自身调用)
  • 存在一种简单的情景,使得递归可以在这个简单情景中退出

分治法的思想:

将整个问题分解成若干小的问题后再分而治之,如果分解得到的子问题相对而言还是太大,则可以反复使用分治策略将这些子问题分成更小的子问题。 

786.第k个数 date 6-27

#include <iostream>
using namespace std;

const int N = 1e5+10;
int n;
int q[N];

int quick_sort_k(int q[],int l,int r,int k){
    if (l==r)return q[l];
    int pivot = q[l+r>>1];
    int i = l-1,j=r+1;
    while(i<j){
        do i++;while(q[i]<pivot);
        do j--;while(q[j]>pivot);
        if (i<j)swap(q[i],q[j]);
    }
    // 根据分界点,将数组分为s1和s2,求得s1的数组大小为s1
    int s1 = j-l+1;
    //根据k值与s1大小进行比较,若小于则向左递归,大于则向右递归
    if (k<=s1)return quick_sort_k(q,l,j,k);
    else return quick_sort_k(q,j+1,r,k-s1);
}
int main(){
    int k;
    scanf("%d %d",&n,&k);
    for (int i=0;i<n;i++)scanf("%d",&q[i]);
    printf("%d",quick_sort_k(q,0,n-1,k));
}

787.归并排序 date 6-30

#include <iostream>

using namespace std;

const int N = 1e5+10;

int q[N];
int temp[N];//作为函数的临时数组
void merge_sort(int q[],int l,int r){
    //处理最简单的递归退出情景
    if (l>=r)return ;

    //1.将数组一分为二
    int mid = l+r>>1;

    //2.分别对左右两个数组进行排序
    //2-1 刚写到这里不要担心还没有排序,递归最后都会以最简单的情景退出
    merge_sort(q,l,mid);
    merge_sort(q,mid+1,r);

    //3.对左右两个排序数组进行合并
    //3-1 你可以想想当数组只有2个的场景,那么左右两边已经排好序了

    //3-2 设置三个指针,分别指向左数组的开始l,右数组的开始mid+1,以及临时数组的开始0,将左右两个数组归并到一个临时数组当中
    int i=l,j=mid+1,k=0;
    while (i<=mid&&j<=r){
        if (q[i]<q[j])temp[k++] = q[i++];
        else temp[k++]=q[j++];
        /*
        1.将q[j]的值赋给temp[k]
        2.递增j
        3.递增k
        */

    }
    //3-2-1 若有一个数组还有其余的值,那么则将这个数组剩下的数值按序插到临时数组的后面
    while (i<=mid)temp[k++]=q[i++];
    while (j<=r)temp[k++]=q[j++];
    //3-3 再将临时数组赋值给q数组的相应位置,此时q数组应从l开始,而临时数组从0开始
    for (i=l,j=0;i<=r;i++,j++)q[i]=temp[j];

}
int main(){
    int n;
    scanf("%d",&n);
    for (int i=0;i<n;i++)scanf("%d",&q[i]);
    merge_sort(q,0,n-1);
    for (int i=0;i<n;i++)printf("%d ",q[i]);
}

 788. 逆序对的数量 date 6.30

逆序对的求解一般会放在归并排序的后面,因为思路一样

归并排序所作的三件事:

  • 排序前1/2的数组
  • 排序后1/2的数组
  • 合并两个1/2数组

逆序对所作的三件事:

  • 计算前1/2数组逆序对的个数
  • 计算后1/2数组逆序对的个数
  • 计算逆序对分别在前后两个数组的个数
#include <iostream>

using namespace std;

const int N = 1e5+10;

typedef long long LL;

int q[N];
int temp[N];
LL merge_sort_nixudui(int q[],int l,int r){
    // 递归最简单的情景
    if (l>=r) return 0;

    // 1.将数组一分为二
    int mid = l+r>>1;// +号的优先级大于>>
    LL res = 0;//结果
    // 2.分别对左右的数组进行逆序对的排序
    // 2-1 这里一定要注意数组的左右边界,左边界不是0而是输入l
    // 2-2 此外不要担心求不出来,可以从最简单的情景往上推
    res = merge_sort_nixudui(q,l,mid) + merge_sort_nixudui(q,mid+1,r);

    // 3.计算逆序对的两个值分别在左右两个数组的情况
    // 3-1 i,j,k分别是左右数组的开始指针和临时数组的开始指针
    int i = l,j=mid+1,k=0;
    while (i<= mid && j<=r){
        // 3-1-1 这里的逻辑是基于左右两边是排好序的,因此归并排序的代码还要写
        if (q[i]>q[j]){
            res += mid-i+1;
            temp[k++] = q[j++];
        }else{
            temp[k++] = q[i++];
        }
    }
    //3-2 若有一个数组还有其余的值,那么则将这个数组剩下的数值按序插到临时数组的后面
    while (i<=mid)temp[k++]=q[i++];
    while (j<=r)temp[k++] = q[j++];

    //3-3 再将临时数组放入赋给q数组
    //3-3-1 i指针指向q数组,j为临时数组temp
    for (int i =l,j = 0;i<=r;i++,j++)q[i]=temp[j];

    return res;

}
int main(){
    int n;
    scanf("%d",&n);
    for (int i=0;i<n;i++)scanf("%d",&q[i]);

    printf("%lld",merge_sort_nixudui(q,0,n-1));

}

 789. 数的范围 date 6.30

二分查找

详细的解析:二分查找为什么总是写错?_哔哩哔哩_bilibili

给出该解法的伪代码:

l = -1, r = N;//数组范围为[0,N-1],但是初始化时使用l=-1,r=N

while (l+1 != r)//循环条件
{
    mid = l+r >> 1;//向下取整
    if (check(m))//check(m)为查找条件
    {
        l = m;
    }
    else{
        r = m;
    }
}

return l or r;//根据要求返回值

#include <iostream>

using namespace std;

const int N = 1e5+10;
int q[N];

int Binary_search1(int q[],int l,int r,int ask){
    l = -1,r = r + 1;
    while (l+1 != r){
        int mid = l+r >> 1;
        if (q[mid]<ask){
            l=mid;
        }else{
            r=mid;
        }
    }
    if (q[r]!=ask)return -1;
    else return r;
}
int Binary_search2(int q[],int l,int r,int ask){
    l = -1,r = r + 1;
    while (l+1 != r){
        int mid = l+r >> 1;
        if (q[mid]<=ask){
            l=mid;
        }else{
            r=mid;
        }
    }
    if (q[l]!=ask)return -1;
    else return l;
}

int main(){
    int n;//数组长度
    int m;//询问个数

    scanf("%d %d",&n,&m);
    for (int i=0;i<n;i++)scanf("%d",&q[i]);
    int ask[m];//询问元素的数组
    for (int i=0;i<m;i++)scanf("%d",&ask[i]);

    for (int i=0;i<m;i++){
        int answer1,answer2;
        answer1 = Binary_search1(q,0,n-1,ask[i]);
        answer2 = Binary_search2(q,0,n-1,ask[i]);
        printf("%d %d\n",answer1,answer2);
    }
    return 0;
}

790. 数的三次方根 date 7.1

 

#include <iostream>

using namespace std;

int main()
{
    double n;
    scanf("%lf",&n);

    double l = -10000,r=10000;
    while (r-l>1e-7){
        double mid = (l+r)/2;
        if (mid*mid*mid>=n)r=mid;
        else l=mid;
    }
    printf("%lf",l);
    return 0;
}

791. 高精度加法 date 7.1

#include <iostream>

#include <vector>

using namespace std;

vector<int> add(vector<int> &A,vector<int> &B){
    vector<int> C;
    int t=0;
    for (int i=0;i<A.size()||i<B.size();i++){
        if (i<A.size())t+=A[i];
        if (i<B.size())t+=B[i];
        C.push_back(t%10);
        t = t/10;

    }
    if (t)C.push_back(t);
    return C;
}

int main(){
    string a,b;
    cin >>a>>b;
    vector<int> A,B;
    for (int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
    for (int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');

    auto C = add(A,B);
    for (int i=C.size()-1;i>=0;i--)printf("%d",C[i]);

    return 0;

}

792. 高精度减法 date 7.1

 

#include <iostream>

#include <vector>
using namespace std;

// 比较两个数大小
bool cmp(vector<int> A,vector<int> B){
    // 两者size不同,直接通过A.size()>B.size()判断大小
    if (A.size()!=B.size())return A.size()>B.size();
    else{
        //两者size()相同,那么则通过从高到低依次比较,直到遇到第一个不相同的数
        for (int i=A.size()-1;i>=0;i--){
            if (A[i]!=B[i])return A[i]>B[i];
        }

    }
    //两者都没能比较出来,那么两者一定相同
    return true;
}

vector<int> sub(vector<int> &A,vector<int> &B){
    int t=0;//定义一个借位t
    //须知 vector 存储的第一位为个位即数“123456”
    //在vector中存储是6 5 4 3 2 1
    vector<int> C;
    for (int i=0;i<A.size();i++){
        // 正常情况下,每个位置输出的值为A[i]-B[i]-t(借位)
        // 由于B.size()比A.size()小,因此需要判断而减
        t = A[i]-t;
        if (i<B.size())t -=B[i];
        C.push_back((t+10)%10);// (t+10)%10满足了借位和不借位两种情况的输出
        if (t<0)t=1;//若t最后小于0,则需向上借位
        else t=0;
    }
    // 会出现00001的情况,那么则需将之前的0都删除,至少保留一位的输出
    // 那么最边界的条件为C只有两位,且高位为0,只有一位时,即使为0也不能再删除
    while (C.size()>1&&C.back()==0) C.pop_back();
    return C;
}
int main(){
    string a,b;
    vector<int> A,B;
    cin>>a>>b;
    for (int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
    for (int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
    if (cmp(A,B)){
        auto C = sub(A,B);
        for (int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
    }else{
        auto C = sub(B,A);
        printf("-");
        for (int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
    }

    return 0;


}

793. 高精度乘法 date 7.1

 

#include <iostream>

#include <vector>

using namespace std;

vector<int> mul(vector<int> A,int b){
    int r=0;//设置一个进位
    vector<int> C;
    for (int i=0;i<A.size();i++){
        r += A[i]*b;
        C.push_back(r%10);
        r/=10;
    }
    if (r!=0)C.push_back(r);//若最后的进位不为0,那么则最为最高位
    while (C.size()>1&& C.back()==0)C.pop_back();
    return C;
}
int main(){
    string a;
    int b;
    cin >> a >> b;
    vector<int> A;
    for (int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');

    auto C = mul(A,b);
    for (int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
    return 0;
}

794. 高精度除法 date 7.1

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

vector<int> div(vector<int> A,int b,int& r){
    r = 0;
    vector<int> C;
    for (int i=A.size()-1;i>=0;i--){
        r = r*10+A[i];
        C.push_back(r/b);
        r=r%b;
    }
    reverse(C.begin(),C.end());//这里是end(),下面是back()
    while (C.size()>1&&C.back()==0)C.pop_back();
    return C;
}
int main(){
    string a;
    int b;

    cin>> a >>b;
    vector<int> A;
    for (int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
    int r;
    auto C = div(A,b,r);
    for (int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
    printf("\n%d",r);
    return 0;
}

826. 单链表 date 7.2

#include <iostream>

using namespace std;

const int N = 1e5+10;
int head;//表示头节点的下标
int e[N];//表示节点i的值
int ne[N];//表示节点i的next指针是多少
int idx;//表示当前可以操作的节点

void init(){
    /*
    head应该是一个特殊的指针,一开始指向-1,表示链表里没有内容,为空节点
    当链表里有元素的时候,它变成了一个指向第一个元素的指针。
    还有就是为了最后遍历链表时,
    知道链表什么时候结束(因为最后实现的链表,它最后一个节点的ne[i]一定等于-1)
    */
    head = -1;

    idx = 0;
}
// 将x插入到头节点
void add_to_head(int x){
    e[idx] = x;
    ne[idx] = head;
    head = idx;
    idx ++;
}
// 将x插入到下标为k的节点后
void add(int k,int x){
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx++;
}
//将下标k后面的节点删除
void remove(int k){
    ne[k] = ne[ne[k]];
}
int main(){
    init();//记得初始化
    int M;//表示操作次数
    scanf("%d",&M);

    while (M--){
        char c;
        cin >> c;
        if (c == 'H'){
            int x;
            scanf("%d",&x);
            add_to_head(x);
        }else if (c == 'D'){
            int k;
            scanf("%d",&k);
            if (k == 0)head = ne[head];//删除头节点
            remove(k-1);//注意删除第k个输入后面的数,那函数里放的是下标,k要减去1

        }else if (c == 'I'){
            int k,x;
            scanf("%d %d",&k,&x);
            add(k-1,x);//同样的,第k个数,和下标不同,所以要减1
        }
    }
    for (int i = head;i != -1;i = ne[i]){
        printf("%d ",e[i]);
    }
    return 0;

}

827. 双链表 date 7.22

#include <iostream>

using namespace std;

const int N = 1e5+10;

int head;
int tail;
int idx;
int e[N];
int l[N];
int r[N];


void init(){
    head = 0;
    tail = 1;
    r[head] = tail;
    l[tail] = head;
    idx = 2;//idx 此时已经用掉两个点了
}
// 在第 K 个点右边插入一个 X 这边的 k 不加 1 , 输入的时候 k+1 就好
void add(int k,int x){
    e[idx] = x;
    l[idx] = k;
    r[idx] = r[k];
    l[r[k]] = idx;
    r[k] = idx;
    idx ++;
}

void remove(int k){
    l[r[k]] = l[k];
    r[l[k]] = r[k];
}


int main(){
    init();
    string c;
    int n;
    scanf("%d",&n);
    while (n--){
        cin >> c;
        int x;
        int k;
        if (c == "L"){
            cin >> x;
            //表示在链表的最左端插入数x,这个最左和最右端是在头和尾节点不动的情况下
            add(0,x);
        }else if (c == "R"){
            cin >> x;
            //表示在链表的最右端插入数x;
            add(l[tail],x);
        }else if (c == "D"){
            cin >> k ;
            //表示把第k个插入的数删掉
            remove(k+1);因为初始化加了两个节点,所以第k个数的下标为k+2-1
        }else if (c == "IL"){
            cin >> k >> x;
            add(l[k+1],x);
        }else if (c == "IR"){
            cin >> k >> x;
            add(k+1,x);
        }

    }
    for (int i = r[head];i!=1;i = r[i]){
        printf("%d ",e[i]);
    }
    return 0;
}




828. 模拟栈 date 7.22

#include <iostream>

using namespace std;

const int N = 1e5+10;
int stk[N],tt=0;

void push(int x){
    stk[++tt] = x;
}

void pop(){
    tt--;
}
int query(){
    return stk[tt];
}

void empty(){

    if (tt)cout<<"NO"<<endl;
    else cout<<"YES"<<endl;
}

int main(){
    int n;
    string op;
    scanf("%d",&n);
    while (n--){
        int x;
        cin>> op;
        if (op == "push"){
            cin >> x;
            push(x);
        }
        if (op == "query"){
            cout<< query()<<endl;
        }
        if (op == "pop"){

            pop();
        }
        if (op == "empty"){
            empty();
        }

    }
    return 0;
}

 3302. 表达式求值 date 7.22

#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
#include <unordered_map>

using namespace std;

//双栈
stack<int> num;
stack<char> op;

//求值函数,使用末尾的运算符来操作末尾的两个数
void eval(){
    auto b = num.top();num.pop();
    auto a = num.top();num.pop();
    auto c = op.top();op.pop();

    int x;
    if (c == '*')x = a*b;
    if (c == '/')x = a/b;
    if (c == '+')x = a+b;
    if (c == '-')x = a-b;
    num.push(x);
}

//(3+5*4/2+2*(1+1)*(2+2))
int main(){
    //设置运算符优先级表
    unordered_map<char,int>pr{ {'+',1}, {'-',1}, {'*',2}, {'/',2}};

    //读入表达式
    string str;
    cin >> str;




    for (int i = 0;i < str.size() ; i++){
        auto c = str[i];
        //如果扫描到了数字,则使用双指针法一直读入
        if (isdigit(c)){
            int x=0,j = i;
            while (j < str.size() && isdigit(str[j])){
                x = x * 10 + str[j++] - '0';
            }
            //这里是因为如果减去,那么for循环中的i++还会加一边
            i = j - 1;
            //数字入栈
            num.push(x);

        }else if (c == '('){
         //左括号直接入栈
            op.push(c);
        }else if (c == ')'){
         //右括号则从右往左计算栈中数据,直至遇见左括号
         //不断使用eval函数对末尾数字运算
            while(op.top() != '(')eval();
            op.pop();
        }else{
            //扫描到运算符
            //如果栈顶运算符优先级较高,那么遇到新的低级运算符
            //就可以先计算站内的数字再将符号入栈
            /*
            while ( pr[c] <= pr[op.top()] && op.size() )eval();
                为什么这样可以运行,但是这样就不可以
                while (  op.size() && pr[c] <= pr[op.top()] )eval();

            */
            while ( op.size() &&  pr[c] <= pr[op.top()] )eval();
            op.push(c);
        }

    }
    //最后将没有操作完的运算符从右往左操作一遍
    while (op.size())eval();
    //栈顶元素为最终的答案
    cout<< num.top()<<endl;

    return 0;
}

 829. 模拟队列 date 7.22

#include <iostream>

using namespace std;

const int N = 1e5+10;
int q[N];
int hh=0;
int tt=-1;


void push(int x){
    q[++tt] = x;
}

void pop(){
    hh++;
}
void empty(){
    if (tt>=hh)cout<<"NO"<<endl;
    else cout<<"YES"<<endl;
}
void query(){
    printf("%d\n",q[hh]);
}


int main(){
    int m;
    scanf("%d",&m);
    string s;
    while (m--){
        cin >> s;
        if (s == "push"){
            int x;
            scanf("%d",&x);
            push(x);
        }else if (s == "pop"){
            pop();
        }else if (s == "empty"){
            empty();
        }else query();


    }

    return 0;
}

 830. 单调栈 date 7.22

#include <iostream>

#include <stack>
using namespace std;
// 单调栈
// 若是单调递增栈,我们则需要维护一个栈中元素从栈底到栈顶是递增的
stack<int> stk;

int main(){
    int n;
    scanf("%d",&n);

    while (n--){
        int k;
        scanf("%d",&k);
        // 那么k需要和栈中元素进行比较,若栈顶元素大于k,则弹出栈顶元素,对下一个进行比较,
        // 直到栈为空或者找到栈顶元素小于k,此时将k压入栈中,那么栈内元素依然保持单调递增
        while (stk.size() && stk.top()>=k)stk.pop();
        if (stk.size())printf("%d ",stk.top());
        else printf("-1 ");
        stk.push(k);

    }
    return 0;

}

154. 滑动窗口 date 7.22

#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>

using namespace std;

const int N = 1e6+10;

int a[N];
int main(){
    int n;
    int k;
    scanf("%d %d",&n,&k);
    for (int i = 1;i <= n;i++)scanf("%d",&a[i]);
    deque<int> q;
    //寻求 滑动窗口的最小值,构造递增队列,队首则为最小值
    for (int i = 1;i <= n;i++){
        while (!q.empty() && q.back() >a[i])q.pop_back();
        q.push_back(a[i]);
        // 此时需要滑动窗口,但是需要满足两个前提条件
        //  1. 当窗口还未满足三个的时候不用滑动,这对应着初始三次遍历,i =1 ,2,3 此时不用滑动 :i - k >= 1
        //  2. 强制滑动,当a[i-k]仍然存在的时候,则将其抛出,因为右边的队列也会抛出,很有可能将其已经抛出,那么如果不加以限制
        // 无脑抛出的话,刚刚队尾加入的元素就立刻被抛出了
        if (i-k>=1 && q.front() == a[i-k])q.pop_front();

        //此时队列满足了单调递增,那么队首就是最小值。
        //此时还需注意,滑动窗口形成至少为k个
        if (i-k>=0)printf("%d ",q.front());

    }
    q.clear();
    cout<<endl;
    //同理 寻求滑动窗口的最大值,构造递减队列,队首则为最大
    for (int i = 1;i<=n;i++){
        while (!q.empty() && q.back() < a[i])q.pop_back();
        q.push_back(a[i]);
        if (i-k>=1 && q.front() == a[i-k])q.pop_front();
        if (i-k>=0)printf("%d ",q.front());

    }
    return 0;


}

 831. KMP字符串 date 7.22

匹配.PNG

简单叙述一下:

当我们匹配到一半,出现了不匹配的字母,传统方法就是模式串P向后移动一位,从头重新一个一个匹配,KMP匹配则是利用了已经匹配的字符串的信息。

既然我们都是要重新匹配,那么如果已经匹配成功的字符串中有相同的前缀和后缀,那么则可以直接利用这个信息,如图,P已经匹配的字符串中存在相同的前缀和后缀1和3。当然1=3=2。你可以想像一下按照传统方法拖拽P模式串,当模式串中的1再次与2重合时,才是真正下次匹配的时候。

那么现在就是如何求得一个字符串的前缀和后缀 

我们可以简单地看这张图,字符串的前后缀计算的递归。

假设我们已经有了字符串[1~15],并且[1~15]的字符串存在前后缀[1~7]和[9~15],

那么我们该如何求得字符串[1~16]的前后缀。

1. 现在已经多了一个字符16,我们让[9~15]加上这个字符构成[9~16],对应着[1~7]加上8字符构成[1~8]。若相等,那么前后缀就是[1~8]和[9~16]。

2.若不相等,那么我们可以向下递归,[1~7]的前后缀为[1~3]和[5~7],[9~15]的前后缀为[9~11]和[13~15]。

且[1~3]、[5~7]、[9~11]和[13~15]四个相等,那么让[13~15]加上16,对应着[1~3]加上4。若相等则[1~16]的前后缀为[1~4]和[13~16]。

若不相等,则向下继续递归。

835. Trie字符串统计 date 7.26

#include <iostream>

using namespace std;
const int N=1e6+10;
int son[N][26];
int idx=0;
int cnt[N];
void insert(string s){
    int p=0;
    for (int i=0;s[i];i++){
        int a = s[i]-'a';
        if (!son[p][a]){
            idx++;
            son[p][a] = idx;
        }
        p = son[p][a];
    }
    cnt[p]++;
}
int query(string s){
    int p=0;
    for (int i = 0;s[i];i++){
        int a = s[i]-'a';
        if (!son[p][a])return 0;
        p = son[p][a];
    }
    return cnt[p];
}
int main(){
    int n;
    cin>>n;
    char op;
    string s;
    while (n--){


        cin>> op>>s;
        if (op == 'I')insert(s);
        else cout<<query(s)<<endl;
    }
    return 0;
}

 143. 最大异或对 date 7.26

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1e5+10;
int son[N*31][2];
int idx;
int a[N];
void insert(int x){
    int p = 0;//不是全局变量需要设置初始值,全局变量会初始化为0
    for (int i=30;i>=0;i--){
        int a = x>>i&1;
        if (!son[p][a])son[p][a] = ++idx;
        p = son[p][a];
    }
}

int search(int x){
    int p = 0;
    int res = 0;
    for (int i=30;i>=0;i--){
        int a = x>>i&1;
        if (son[p][!a]){
            p = son[p][!a];
            res = res*2+1;
        }else{
            p = son[p][a];
            res = res*2+0;
        }
    }
    return res;
}
int main(){
    int n;
    cin >> n;
    for (int i = 0;i<n;i++){
        cin >> a[i];
        insert(a[i]);
    }

    int res = 0;
    for (int i=0;i<n;i++){
        res = max(res,search(a[i]));
    }

    cout<<res<<endl;

}

 836. 合并集合 date 7.26

#include <iostream>

using namespace std;

const int N =1e5+10;

int p[N];//为某个数的父亲节点

// 寻找x的祖宗节点
int find(int x){
    if (p[x]!=x)p[x] = find(p[x]);
    return p[x];
}



int main(){
    int n,m;
    cin >> n>>m;

    // 刚开始每个数的父亲节点都指向自己
    for (int i = 0;i<n;i++)p[i] = i;
    while (m--){
        char op;
        int a,b;
        cin>>op>>a>>b;
        if (op == 'M'){
            if (find(a)!=find(b))p[find(b)]=find(a);
        }else{
            if (find(a)==find(b))cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        
        }
        
        
    }


    return 0;

}

 837. 连通块中点的数量 date 7.26

#include <iostream>

using namespace std;

const int N = 1e5+10;

int p[N];//祖宗节点
int size1[N];

//寻找祖宗节点
int find(int x){
    if (p[x] != x)p[x] = find(p[x]);
    return p[x];
}


int main(){
    int n,m;
    cin >> n >> m;
    //init
    for (int i=1;i<=n;i++){
        p[i] = i;
        size1[i] = 1;
    }
    while (m--){
        string op;
        int a,b;
        cin >> op;
        if (op[0] == 'C'){
            cin>>a>>b;
            if (find(a) != find(b)){
                size1[find(b)]+=size1[find(a)];//这两句的顺序不能换,先得赋值,不然的话祖宗节点变了,那么size就变了
                p[find(a)] = find(b);
            }
            }else if (op[1] == '1'){
                cin>>a>>b;
                if (find(a)==find(b))cout<<"Yes"<<endl;
                else cout<<"No"<<endl;
            }else {
                cin>>a;
                cout<<size1[find(a)]<<endl;
            }
    }
    return 0;
}

 838. 堆排序 date 7.26

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1e5+10;
int h[N];
int mysize;

void down(int i){
    int temp = i;
    if (2*i<=mysize && h[2*i]<h[temp])temp = 2 * i;
    if (2*i+1<=mysize && h[2*i+1]<h[temp])temp=2*i+1;
    if (temp != i){
        swap(h[temp],h[i]);
        down(temp);
    }

}
int main(){
    int n,m;
    cin >> n >> m;
    mysize = n;
    for (int i=1;i<=n;i++)cin >> h[i];

    for (int i = n/2;i;i--)down(i);

    while (m--){
        cout<<h[1]<<" ";
        h[1] = h[mysize];
        mysize--;
        down(1);
    }


}

839. 模拟堆 date 7.27

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1e5+10;
int h[N];
int mysize;
// 既然我们使用堆进行模拟,而题中又要求第k个插入的数
// 因此需要建立
int ph[N];//ph[i]表示第i插入的数在堆中的下标
int hp[N];//hp[j]表示堆中第j个数是当初第i个插入的数

void heap_swap(int a,int b){
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a],hp[b]);
    swap(h[a],h[b]);
}
void down(int i){
    int t = i;
    if (2*i<=mysize && h[2*i]<h[t])t =  2*i;
    if (2*i+1<=mysize && h[2*i+1]<h[t])t = 2*i+1;
    if (t != i){
        heap_swap(i,t);
        down(t);
    }
}


void up(int i){
    int t = i;
    if (i/2>=1 && h[t]<h[i/2])t = i/2;
    if (i != t){
        heap_swap(t,i);
        up(t);
    }
}

int main(){
    int m;
    cin>>m;
    int cnt=0;//表示第i个插入的数
    while (m--){
        string op;
        cin >> op;
        if (op[0] == 'I'){
            int x;
            cin >> x;
            cnt ++ ;
            mysize++;
            ph[cnt] = mysize;
            hp[mysize] = cnt;
            h[mysize] = x;
            up(mysize);
        }else if (op[0] == 'P'){
            cout<<h[1]<<endl;
        }else if (op[0] == 'D' && op[1] == 'M'){
            heap_swap(1,mysize);
            mysize -- ;
            down(1);
        }else if (op[0] == 'C'){
            int k,x;
            cin >>k>>x;
            h[ph[k]] = x;
            up(ph[k]);
            down(ph[k]);
        }else {
            int k;
            cin >>k;
            int w = ph[k];
            heap_swap(ph[k],mysize);
            mysize --;
            down(w);
            up(w);
        }

    }

    return 0;
}

842. 排列数字 date 7.27

其实基本的思想大家都懂,但是写代码就有点怵。

借用一下题解哥们的图

呃,这不就是循环嘛,

#include <iostream>
using namespace std;

const int N=8;
int path[N];

int state[N];
int n;
void dfs(int u){
    if (u > n){
        for (int i=1;i<=n;i++)cout<<path[i]<<" ";
        cout<<endl;
        return;
    }
    
    for (int i = 1;i<=n; i++){
        if (!state[i]){
            path[u] = i;
            state[i] = 1;
            dfs(u+1);
            state[i] =0;
        }
        
    }
}
int main(){
    cin >>n;
    dfs(1);
    
    return 0;
}

 843. n-皇后问题 date 7.27

#include <iostream>
using namespace std;

const int N=11;
int path[N];

int state[N],dg[N*2],udg[N*2];
int n;
void dfs(int u){
    if (u > n){
        for (int i=1;i<=n;i++){
            for (int j=1;j<=n;j++){
                if (j == path[i])cout<<"Q";
                else cout<<".";
                if (j == n)cout<<endl;
            }
        }
        cout<<endl;
        return ;
    }

    for (int i = 1;i<=n;i++){
        if (!state[i] && !dg[u+i] && !udg[u-i+n]){
            path[u] = i;
            dg[u+i] = 1;
            udg[u-i+n] = 1;
            state[i] = 1;
            dfs(u+1);
            state[i] = 0;
            dg[u+i] = 0;
            udg[u-i+n] = 0;
        }

    }
}
int main(){
    cin >> n;
    dfs(1);

    return 0;
}

844. 走迷宫 date 7.27

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

const int N = 110;

// 存储地图
int g[N][N];
// 存储坐标x,y到左上角的距离
int f[N][N];
typedef pair<int,int> PII;

int n,m;


void bfs(int a,int b){
    queue<PII> q;
    q.push({a,b});

    int dx[4] = {0,0,-1,1};
    int dy[4] = {-1,1,0,0};

    while (!q.empty()){
        int x = q.front().first;
        int y = q.front().second;
        q.pop();

        for (int i=0;i<4;i++){
            int x_ = x + dx[i];
            int y_ = y + dy[i];
            if (x_>=1 && x_<=n && y_>=1 && y_<=m && g[x_][y_] == 0){
                g[x_][y_] = 1;
                f[x_][y_] = f[x][y] + 1;
                q.push({x_,y_});
            }

        }
    }

}
int main(){
    memset(g,1,sizeof(g));
    cin >> n >>m;
    for (int i = 1;i<=n;i++)
        for (int j=1;j<=m;j++)
            cin>>g[i][j];
    bfs(1,1);
    cout<<f[n][m]<<endl;


}

845. 八数码 date 7.27

#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_map>
#include <queue>

using namespace std;

const int N = 50;
unordered_map<string,int> cnt;
queue<string> q;
int main(){
    string s;
    for (int i = 0;i<9;i++){
        char c;
        cin >> c;
        s += c;
    }
    q.push(s);
    cnt[s] = 0;
    int dx[4] = {0,0,-1,1};
    int dy[4] = {-1,1,0,0};

    while (!q.empty()){
        string t = q.front();
        q.pop();

        if (t == "12345678x"){
            cout<< cnt[t];
            return 0 ;
        }
        int pos = t.find('x');
        int x = t.find('x')/3;
        int y = t.find('x')%3;

        for (int i=0;i<4;i++){
            int x_ = x + dx[i];
            int y_ = y + dy[i];
            if (x_>=0 && x_<=2 && y_>=0 && y_<=2){
                int pos_ = x_*3 + y_;
                string t_ = t;
                swap(t_[pos],t_[pos_]);
                if (cnt.find(t_) == cnt.end()){
                    cnt[t_] = cnt[t] + 1;
                    q.push(t_);
                }

            }
        }


    }
    cout<<"-1";
    return 0;



}

846. 树的重心 date 7.28

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5+10;

int head[N];
int e[N*2];
int ne[N*2];
int idx;
int state[N];
int ant = N;//最终的各个连通块中点数的最大值最小值
int n;

// a--->b
void add(int a,int b){
    e[idx] = b;
    ne[idx] = head[a];
    head[a] = idx++;
}

// 以a为根的子树的节点数量,这里的a不是idx
int dfs(int a){

    state[a] = 1;
    int sum = 1;
    int res = 0;
    for (int i = head[a]; i != -1; i = ne[i]){
        int j = e[i];//从idx获得节点
        if (!state[j]){
            int s = dfs(j);
            res = max(res,s);
            sum+=s;

            /*
            int s = dfs(j);
            res = max(res,s);
            sum+=s;

            res = max(res,dfs(j));
            sum +=dfs(j);//再次访问dfs(j)与上一行的dfs(j)结果不同
            深度递归,会改变全局变量的访问值
            */
        }
    }
    //这里的sum其实就是dfs(a)
    res = max(res,n-sum);
    ant = min(ant,res);
    return sum;

}

int main(){
    memset(head,-1,sizeof(head));

    cin >> n;
    for (int i = 0; i < n - 1; i++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
        add(b, a);

    }
    dfs(2);
    cout<< ant <<endl;
    return 0;

}

847. 图中点的层次 date 7.28

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1e5+10;

int head[N];
int e[N];
int ne[N];
int idx;
int d[N];
queue<int> q;
int state[N];

void add(int a,int b){
    e[idx] = b;
    ne[idx] = head[a];
    head[a] = idx++;
}

int main(){
    memset(head,-1,sizeof head);
    int n,m;
    cin >> n >> m;
    for (int i=0;i<m;i++){
        int a,b;
        cin >> a >> b;
        add(a,b);
    }

    q.push(1);

    state[1] = 1;
    while (!q.empty()){
        int u = q.front();
        
        q.pop();
        for (int i = head[u]; i != -1;i = ne[i]){
            int j = e[i];
            if (!state[j]){
                q.push(j);
                d[j] = d[u] + 1;
                state[j] = 1;
            }
        }

    }
    if (d[n]==0 && n!=1){
        cout<<"-1"<<endl;
    }else{
        cout<< d[n]<<endl;
    }
    return 0;


}

 848. 有向图的拓扑序列 date 7.28

#include <iostream>
#include <queue>
#include <cstring>
#include <vector>
using namespace std;
const int N = 1e5+10;
vector<int> head[N];
queue<int> q;
int ans[N];
int cnt;
int d[N];
void add(int a,int b){
    head[a].push_back(b);
    d[b]++;
}
int main(){
    int n,m;
    cin >> n >> m;

    for (int i=0;i<m;i++){
        int a,b;
        cin >> a >>b;
        add(a,b);
    }

    for (int i=1;i<=n;i++){
        if (!d[i])q.push(i);
    }

    while (!q.empty()){
        int t = q.front();
        q.pop();
        ans[cnt++] = t;
        for (int i =0;i<head[t].size();i++){
            int j = head[t][i];
            d[j] -- ;
            if (!d[j])q.push(j);
        }

    }
    if (cnt == n)
        for (int i=0;i<cnt;i++)cout<<ans[i]<<" ";
    else cout<<"-1";
    return 0;


}

849. Dijkstra求最短路 date 7.28 

稀疏图 邻接矩阵 

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 510,M = 1e5+10;

int e[M];
int ne[M];
int w[M];
int head[N];

int dist[N];
int state[N];
int idx;
int n,m;
void add(int a,int b,int c){
	e[idx] = b;
	ne[idx] = head[a];
	w[idx] = c;
	head[a] = idx++;
}
void dijkstra(){
	dist[1] = 0;
	int t = 0;
	for (int j = 0;j<n;j++){
                int t = -1;
        for (int i = 1; i <= n; i++)//遍历 dist 数组,找到没有确定最短路径的节点中距离源点最近的点t
        {
            if (!state[i] && (t == -1 || dist[i] < dist[t]))
                t = i;
        }
		
		state[t] = 1;
		for (int i = head[t];i != -1;i = ne[i]){
			int k = e[i]; 
			if (dist[k] > (dist[t] + w[i])){
				dist[k] = dist[t] + w[i];
			}
		}
		
	
	}
	
}
int main(){
	memset(dist,0x3f,sizeof dist);
	memset(head,-1,sizeof head);
	cin >> n >> m;
	for (int i=0;i<m;i++){
		int a,b,c;
		cin >> a >> b >>c;
		add(a,b,c); 
	}
	dijkstra();
	if (dist[n] == 0x3f3f3f3f){
		cout <<"-1";
	}else{
		cout << dist[n];
	}
	
	
} 

稠密图 数组

#include <bits/stdc++.h>

using namespace std;

int n,m,c1,c2;
const int N =510;
int state[N];//记录是否源点到该节点的最短距离
int dist[N];//保存源点到该节点的距离
int e[N][N];//来存储不同节点之间的长度
int num_of_city[N];//用来存储每个城市的救援人员数量

void dijkstra(int c){

    dist[c] = 0;//源点到源点的距离为0;
    int t = c;//从c这个节点开始进行迪杰斯特拉算法
    for (int i=0;i<n;i++){
        //找到dist数组中最小且state为未被访问的节点
        if (i!=0){
            //当然第一次直接在源点进行
            int min = 999999;
            for (int j=0;j<n;j++){
                if (!state[j] && dist[j]< min )t = j,min = dist[j];
            }
        }


        //cout<<t;
        state[t] = 1;//将其记录为访问过
        
        for (int j=0;j<n;j++){
            if (j!=t)dist[j] = min(dist[t]+e[t][j],dist[j]);
        }
        
        
        
    }
    

}
int main(){
    memset(dist,0x3f,sizeof dist);
    memset(e,0x3f,sizeof e);
    cin >> n >> m ;
    
    //for (int i=0;i<n;i++)cin>>num_of_city[i];
    for (int i=0;i<m;i++){
        int a,b,l;
        cin>> a >>b>>l;
        e[a-1][b-1]= min(l,e[a-1][b-1]);
    }
    dijkstra(0);
    if (dist[n-1]==0x3f3f3f3f)cout<<-1;
    else cout<<dist[n-1];


}

850. Dijkstra求最短路 II 堆优化 date 7.29

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1e6;

int ne[N];
int e[N];
int w[N];
int head[N];
int idx;
int dist[N];
int state[N];
int n,m;
typedef pair<int,int> PII;//第一个为距离,第二个为顶点编号u,相当于dist[u]
void add(int a,int b,int c){
    e[idx] = b;
    w[idx] = c;
    ne[idx] = head[a];
    head[a] = idx++;
}
void dijkstra(){
    priority_queue<PII,vector<PII>,greater<PII>> heap;
    heap.push({0,1});
    dist[1] = 0;

    while (!heap.empty()){
        auto t = heap.top();
        heap.pop();
        int u = t.second;// 顶点编号
        int d = t.first;
        if (state[u]) continue;
        state[u] = 1;
        //cout<< u<<endl;
        for (int i = head[u];i != -1;i = ne[i]){
            int j = e[i];
            if (dist[u] + w[i] < dist[j]){
                dist[j] = dist[u] + w[i];
                heap.push({dist[j],j});
            }
        }
    }


}
int main(){
    memset(head,-1,sizeof head);
    memset(dist,0x3f,sizeof dist);
    cin >> n >>m;

    for (int i=0;i<m;i++){
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c);
    }
    dijkstra();
    if (dist[n] == 0x3f3f3f3f) cout<<"-1";
    else cout << dist[n];
    return 0;
}

854. Floyd求最短路 date 7.29

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 210, INF = 1e9;

int n, m, Q;
int d[N][N];

void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

int main()
{
    scanf("%d%d%d", &n, &m, &Q);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;

    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        d[a][b] = min(d[a][b], c);
    }

    floyd();

    while (Q -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);

        int t = d[a][b];
        if (t > INF / 2) puts("impossible");
        else printf("%d\n", t);
    }

    return 0;
}

866. 试除法判定质数 date 7.30

<=1 不是质数

>=1 且出现本身以外其他的因数

优化:

一个数 x 分解成两个数的乘积,则这两个数中,一定有一个数大于根号 x,一个数小于根号x。

因此判断条件为i<=a/i 不要写成i<= sqrt(a),这样计算会慢

#include <iostream>
#include <algorithm>

using namespace std;

int isprime(int a){
    if (a<=1)return 0;
    
    for (int i = 2;i <= a / i;i++){
        if (a%i == 0)return 0;
    }
    return 1;
}
int main(){
    int n;
    cin >> n;
    while (n--){
        int a;
        cin >> a;
        if (isprime(a)) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;

}

867. 分解质因数 date 7.30

假设我们要分解的正整数是 N。

  1. 首先,找到 N 的最小质因数 p(p > 1)。如果 N 是质数,则 N 本身就是最小的质因数,质因数分解完成。

  2. 若 N 不是质数,则我们找到了它的最小质因数 p。输出 p 作为底数,指数为 1。

  3. 然后,我们将 N 除以 p,得到一个新的数 N'。

  4. 重复步骤 1 和 2,直到 N' 是一个质数为止。这时输出 N' 作为底数,指数为 1。

  5. 最后的结果就是所有找到的质因数及其指数。

下面是一个示例:

假设我们要分解的正整数是 N = 360。

  1. N = 360,最小质因数是 p = 2。输出 2^1。

  2. 将 N 除以 2,得到 N' = 360 / 2 = 180。

  3. N' = 180,最小质因数是 p = 2。输出 2^1。

  4. 将 N' 除以 2,得到 N'' = 180 / 2 = 90。

  5. N'' = 90,最小质因数是 p = 2。输出 2^1。

  6. 将 N'' 除以 2,得到 N''' = 90 / 2 = 45。

  7. N''' = 45,最小质因数是 p = 3。输出 3^1。

  8. 将 N''' 除以 3,得到 N'''' = 45 / 3 = 15。

  9. N'''' = 15,最小质因数是 p = 3。输出 3^1。

  10. 将 N'''' 除以 3,得到 N''''' = 15 / 3 = 5。

  11. N''''' = 5,5 是质数。输出 5^1。

质因数分解完成。最终的结果是:

360 = 2^3 × 3^2 × 5^1

注意当我们进行质因数分解时,要找到一个数的质因子,通常情况下这个质因子不会大于这个数的平方根。这个数首先是因子,其次是质数,是因子就可以i<=a/i寻找,这样可以提高效率,因为如果存在一个大于平方根的质因子,那么它对应的另一个因子必然小于平方根,这样的话两个因子的乘积就会大于这个数,与给定数的性质相矛盾。

#include <iostream>
#include <vector>;

using namespace std;
typedef pair<int,int> PII;
void primeFactorization(int a){
    vector<PII> factor;
    for (int i=2;i<=a/i;i++){

        if (a%i == 0){
            int sum = 0;
            while (a%i == 0){
                sum ++;
                a = a/i;
            }

            factor.push_back(make_pair(i,sum));//最后没能到1,例如43
        }
        
    }
    if (a > 1)factor.push_back(make_pair(a,1));

    for (int i=0;i<factor.size();i++){
        cout<<factor[i].first<<" "<<factor[i].second<<endl;;
    }
}
int main(){
    int m;
    cin >> m;
    while (m--){
        int a;
        cin >> a;
        primeFactorization(a);
        cout<<endl;

    }
    return 0;

}

868. 筛质数 date 7.30

这题有三个解法,时间复杂度依次降低。

1.最普通的筛法 O(nlogn)

非常的简单,例如:

2  3  4  5  6  7  8  9 10 11 12

yes表示筛过数

第一轮:2-yes  3  4-yes  5  6-yes  7  8-yes  9  10-yes  11  12-yes 

第二轮:2-yes  3-yes  4-yes  5  6-yes  7  8-yes  9-yes  10-yes  11  12-yes 

第三轮:2-yes  3-yes  4-yes  5  6-yes  7  8-yes  9-yes  10-yes  11  12-yes

第四轮:2-yes  3-yes  4-yes  5-yes  6-yes  7  8-yes  9-yes  10-yes  11  12-yes

.....

#include <iostream>
using namespace std;
const int N = 1e6+10;
int is_heshu[N];
int numbers;
void get_primes(int n){
    for (int i = 2;i<=n;i++){
        if (!is_heshu[i])is_heshu[i] = 1,numbers ++;
        
        for (int j = i;j <=n;j+=i){
            is_heshu[j] = 1;
        }
    }
}
int main(){
    int n;
    cin >> n;
    get_primes(n);
    cout<< numbers<<endl;
}

2.诶氏筛法 O(nloglogn)

显然,我们只需要对质数进行向后筛就行,

i= 2(质数)筛一遍,那么当i=4(合数)时,显然都已经被i=2筛过了

代码直接将筛选的过程移到当i=质数时就ok。

#include <iostream>
using namespace std;
const int N = 1e6+10;
int is_heshu[N];
int numbers;
void get_primes(int n){
    for (int i = 2;i<=n;i++){
        if (!is_heshu[i]){
            is_heshu[i] = 1,numbers ++;
            for (int j = i;j <=n;j+=i){
                is_heshu[j] = 1;
        }
        }
        

    }
}
int main(){
    int n;
    cin >> n;
    get_primes(n);
    cout<< numbers<<endl;
}

3.线性筛法(难以理解)

原理是只被筛一次,事实上,i = 3 和i = 11 都会筛33这个数。

所以原理就是只用最小质因数筛33 

这里的筛选规则也略有不同,可以举个例子:

2  3  4  5  6  7  8  9 10 11 12

我们使用primes[]来储存在i之前已经筛出来的质数

通过遍历primes里的每一个数和i的乘积 来筛选所有的数

#include <iostream>
#include <vector>
using namespace std;
const int N = 1e6+10;
int is_heshu[N];

vector<int> primes;//当然用数组也行,需要一个idx记录罢了,primes[idx++] = xxx;
void get_primes(int n){
    for (int i = 2;i<=n;i++){
        if (!is_heshu[i])is_heshu[i] = 1,primes.push_back(i);
        for (const auto &prime :primes){
            if (prime * i >n )break;
            is_heshu[prime * i] = 1;
            if (i%prime == 0)break;
        }
        

    }
}
int main(){
    int n;
    cin >> n;
    get_primes(n);
    cout<< primes.size()<<endl;
}

我就简单讲一下

12这个数被筛

               prime * i

是在12 = 2        * 6 而不是 12 = 3 * 4时被筛的。

if (i%prime == 0)break;意思就是为了最小质因子。

869. 试除法求约数 date 7.30  

约数概念:

如果一个正整数 a 能够被正整数 b 整除(即 a mod b 等于 0),那么 b 就是 a 的约数。

举例来说,对于正整数 12,它的所有约数为:1, 2, 3, 4, 6, 12。因为 1、2、3、4、6、12 都能整除 12,没有其他正整数能够整除 12。

要注意的是,所有的正整数都有两个特殊的约数:1 和它本身。这两个约数被称为它的“边界约数”。对于质数来说,除了边界约数 1 和它本身之外,它没有其他约数。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;


void get_factor(int a){
    vector<int> res;

    for (int i=1;i<=a/i;i++){
        if (a % i == 0){
            res.push_back(i);
            if (a/i != i)res.push_back(a/i);
        }
    }
    sort(res.begin(),res.end());
    for (const auto &r :res)cout<<r<<" ";
}
int main(){
    int m;
    cin >> m;
    for (int i=0;i<m;i++){
        int a;
        cin >> a;
        get_factor(a);
        cout<<endl;
    }
}

870. 约数个数 date 7.30 

就是分解质因数的应用,不过这题注意一下范围,数比较大。

把一个数N 写成:N = (p1^x1^)(p^x2)(p3^x3)…(pk^xk),其中pi为质数。则N的约数个数为:(x1+1)(x2+1)(x3+1)…(xk+1)

#include <iostream>
#include <unordered_map>

using namespace std;
const int N =1e9+7;
unordered_map<int,int> primes;

void get_primes(int a){
    for (int i =2 ;i<=a/i;i++){
        if (a%i == 0){
            
            while (a%i == 0)primes[i]++,a = a/i;
        }
    }
    if (a > 1)primes[a]++;

}
int main(){
    int m;
    cin >> m;
    while(m--){
        int a;
        cin >> a;
        get_primes(a);
    }
    long long  number=1;
    for (const auto& p:primes)number*=(p.second+1),number = number % N;
    
    cout<< number;
    

}

871. 约数之和 date 7.30

#include <iostream>
#include <unordered_map>

using namespace std;
const int N =1e9+7;
unordered_map<int,int> primes;

void get_primes(int a){
    for (int i =2 ;i<=a/i;i++){
        if (a%i == 0){
            
            while (a%i == 0)primes[i]++,a = a/i;
        }
    }
    if (a > 1)primes[a]++;

}
long long S(int p,int k){
    if (k == 0)return 1;
    long long s = (S(p,k-1)*p+1)%N;

    return s;
}
int main(){
    int m;
    cin >> m;
    while(m--){
        int a;
        cin >> a;
        get_primes(a);
    }
    long long  number=1;
    for (const auto& p:primes)number*=S(p.first,p.second),number = number % N;
    
    cout<< number;
    

}

872. 最大公约数 date 7.30

(a,b) = (b,a%b)

到时候忘记举个例子就行;

(8,6)=(6,2)=(2,0)

return 2;

#include <iostream>
#include <algorithm>

using namespace std;

//(a,b) = (b,a%b)
int gcd(int a,int b){
   while (b){
        int tem =a;
        a = b;
        b = tem%b;
   }
   return a;
}

int main(){
    int m;
    cin >> m;
    while (m--){
        int a,b;
        cin >> a>>b;
        cout<<comm(a,b)<<endl;
    }
    return 0;

}

 873. 欧拉函数 date 7.30

 两个或多个整数的最大公约数(GCD)为 1,则它们被称为互质(coprime)或互素。换句话说,如果两个整数没有除了 1 以外的公约数,那么它们就是互质的。

这道题首先注意两点:

分解质因子的条件为i<=a/i,不然会超时。

另外计算最后的互质个数时先除后乘ans =ans/v.first*(v.first-1);不然会越界。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

typedef pair<int,int> PII;
vector<PII> get_primes(int a){
    vector<PII> vs;
    for (int i=2;i<=a/i;i++){
        if (a%i==0){
            int sum =0;
            while (a%i==0)sum++,a = a/i;
            vs.push_back(make_pair(i,sum));
            
        }
    }
    if (a>1)vs.push_back(make_pair(a,1));
    return vs;
}
int q(int a,vector<PII> vs){
    int ans = a;
    for (const auto& v:vs){
        ans =ans/v.first*(v.first-1);
    }
    return ans;

}

int main(){
    int m;
    cin >> m;
    while (m--){
        int a;
        cin >> a;
        vector<PII> vs = get_primes(a);

        cout<<q(a,vs)<<endl;
    }
    return 0;


}

 ACWing第五讲 动态规划

动态规划题的大致流程:

1.设计状态

2.写出状态转移方程

3.设定初始状态

4.执行状态转移

5.返回最终的解

 01背包问题 date 7.31

这里背包里的总价值与物体个数和背包容量均有关系。

因此可以设二维数组dp[i][j]来表示其状态,含义为:在前i个物体中,背包容量为j的情况下,背包总价值最大。

那么dp[i][j]如何计算?
(1)如果第i个物体被选,那么dp[i][j]就可以从前i-1个物体,背包容量为j-weight[i]推导 
即:dp[i-1][j-weight[i]] + value[i];
(2)如果第i个物体没有被选,那么意味着从前i-1个物体中选择,dp[i][j] = dp[i-1][j]

dp[i][j]应该从这两个选择中选择值大的
因此递推公式:
d[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]] + value[i])

但是这种情况,是在背包容量可以容纳第i个物体,下标j-weight[i]大于0.
如果背包容量不足以容纳第i个物体,那么d[i][j]只能为dp[i-1][j]

因此整体的思路应该为:

if (背包容量大于第i个物体)
    d[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]] + value[i]);
else if (背包容量小于第i个物体)
    d[i][j] = dp[i-1][j];

#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1010;
int weight[N];
int value[N];
int dp[N][N];
void DP(int n,int v){
    for (int i=1;i<=n;i++){
        for (int j=1;j<=v;j++){
            if (j<weight[i])dp[i][j] = dp[i-1][j];
            else dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
        }
    }
}
int main(){
    int n,v;
    cin >> n >> v;
    for (int i=1;i<=n;i++){
        int a,b;
        cin >> a>> b;
        weight[i] = a;
        value[i] = b;
    }
    DP(n,v);
    cout<<dp[n][v]<<endl;
    return 0;
}

leetcode 乱刷

70.爬楼梯 date 8.4

1342. 将数字变成 0 的操作次数 date 8.4

LCR 088. 使用最小花费爬楼梯 date 8.4

198. 打家劫舍 date 8.4

213. 打家劫舍 II date 8.4

91. 解码方法 date 8.4

1646. 获取生成数组中的最大值 date 8.4

3. 无重复字符的最长子串 date 8.4

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char,int> m;
        int left = 0,max_len=0;
        for (int i=0;i<s.size();i++){
            if (m.find(s[i])==m.end()){
                m[s[i]] = i;             
            }else {
                if (m[s[i]]>=left)
                {
                    left = m[s[i]]+1;
                }
                m[s[i]] = i; 
            }
            max_len = max(max_len,i-left+1);
        }
        return max_len;
    }
};

1043. 分隔数组以得到最大和 date 8.4

class Solution {
    #define maxn 510
    int dp[maxn];
public:
    int maxSumAfterPartitioning(vector<int>& arr, int k) {
        int max_1=0;
        for (int i=0;i<k;i++){
            max_1 = max(arr[i],max_1);
            dp[i] = max_1 * (i+1);
        }
        cout<<dp[2]<<endl;
        for (int i=k;i<arr.size();i++){//大循环
            int cnt = k;
            while(cnt){//子数组大小分别为1 ......cnt
                int max_cnt = 0;
                for (int j = i-cnt+1;j<=i;j++)max_cnt = max(max_cnt,arr[j]);
                dp[i] = max(dp[i],max_cnt*cnt+dp[i-cnt]);
                cout<<dp[i]<<endl;
                cnt--;
            }
            
        }
        return dp[arr.size()-1];
    }
};

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值