历时一个月,四万字:蓝桥杯重点算法全解析

历时一月:蓝桥杯C++重点算法-梳理(第一次参赛)

你好,读这篇文章的人,我是一个经管类的文科生,但是我一直在学习代码相关,未来想要做一名C++后端工程师,这个是我的个人笔记,可能叙述引用很多不恰当的地方,还请多多包涵,这一个月来我尽我所能完成了我认为我能做到的acwing的c++蓝桥杯课程中的视频,希望看这个博客的人你也能在蓝桥杯中取得好成绩,未来我会持续分享我的经历以及学习笔记,如果喜欢或者觉得有用的话,给我点个关注,谢谢!!!!!
这是目录哦!

一,DFS递推

92. 递归实现指数型枚举 - AcWing题库

image

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

using namespace std;

const int N=9;
int n;
int st[N];//st代表状态,1是说选中了,0是未选中状态,2是归位状态
void dfs(int u)
{
    if (u > n)
    {
        for (int i = 1; i <= n; i ++ )
            if (st[i] == 1)
            {
                printf("%d ", i);
            }
        puts("");
        return;
    }
    
    st[u]=0;//不选的状态
    dfs(u+1);
    st[u]=2;//回复现场
    
    st[u]=1;//选的状态
    dfs(u+1);
    st[u]=2;//回复现场
}

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

2.94. 递归实现排列型枚举 - AcWing题库

[外链图片转存中…(img-BZzy4Vn1-1711015082490)]

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

using namespace std;

const int N=9;
int n;
int st[N];//st代表状态,0表示还没有放数,1->n代表放了什么数
bool used[N];//true表示用过,false表示没用过
void dfs(int u)
{
    if(u>n)
    {
        for(int i=1;i<=n;i++)
        {
            printf("%d ",st[i]);
        }
        puts("");
    }
    // 依次枚举每个数,即当前位置可以填哪些数
    for(int i=1;i<=n;i++)
    {
        if(!used[i])//没有用过
        {
            st[u]=i;//填入数据
            used[i]=true;//已经用过
            
            dfs(u+1);//递归下一个
            
            st[u]=0;//放的数
            used[i]=false;
        }
    }
}

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

3.93. 递归实现组合型枚举 - AcWing题库

C++中next_permutation函数的使用方法、原理及手动实现_c++中next_permutation用法-CSDN博客

image

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

using namespace std;
const int N=30;//way[u]其中u最大为30,u是代表第几个数
int n,m;//从n中选择m个数
int way[N];

void dfs(int u,int start)
{
    //额外补充:剪纸操作,例如当 4 占据第一个位置的时候实际上已经可以不用进行操作了
    //公式:1.已经使用了u-1个数 距离n也就是数的上限还差 n-start+1
    //      2.u-1+n-start+1<m 的时候就应该进行剪枝操作了
    if(u+n-start<m) return;
    if(u-1==m)//u-1代表已经选取了u-1个数恰好等于m的时候,就可以返回结果了
    {
        for(int i=1;i<=m;i++)
        {
            cout<<way[i]<<" ";
        }
        cout<<endl;
    }
    //进行递归操作
    for(int i=start;i<=n;i++)
    {
        way[u]=i;
        dfs(u+1,i+1);//下一个要使用的u就是u+1;start从i+1开始
        way[u]=0;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    dfs(1,1);//第二个也就是start记录了开始的数字
    return 0;
}

4.1209. 带分数 - AcWing题库

a+b/c ==target

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

using namespace std;
int cnt=0;
int num[9]={1,2,3,4,5,6,7,8,9};

int calculate(int a,int b)//这一步是为了计算隔板得到的具体数值比如1 2 3,要计算为123;
{
    int res=0;
    for(int i=a+1;i<=b;i++)
    {
        res=res*10+num[i];
    }
    return res;
}
int main()
{
    int target;cin>>target;
    do{
        for(int i=0;i<9;i++)
        {
            for(int j=i+1;j<9;j++)//两个for循环成为隔板
            {
                int a=calculate(-1,i);//第一个数
                int b=calculate(i,j);
                int c=calculate(j,8);
                if(target*c==a*c+b) cnt++;//进行判断
            }
        }
    }while(next_permutation(num,num+9));//这个permutation产生新的全排列,而里面的两层循环就是进行隔板法!
    //隔板法分成-1+1,i | i+1,j | j+1,8
    cout<<cnt<<endl;
    return 0;
}

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

using namespace std;
int cnt=0;
int target;

int num[10];//保留全排列的结果
bool used[10];//生成全排列过程中标记是否使用

int calculate(int a,int b)//这一步是为了计算隔板得到的具体数值比如1 2 3,要计算为123;
{
    int res=0;
    for(int i=a+1;i<=b;i++)
    {
        res=res*10+num[i];
    }
    return res;
}
void dfs(int u)
{
    if(u==9)
    {
        for(int i=0;i<9;i++)
        {
            for(int j=i+1;j<9;j++)//两个for循环成为隔板
            {
                int a=calculate(-1,i);//第一个数
                int b=calculate(i,j);
                int c=calculate(j,8);
                if(target*c==a*c+b) cnt++;//进行判断

            }
        }
        return;
    }
    for(int i=1;i<=9;i++)
    {
        if(!used[i])
        {
            used[i]=true;
            num[u]=i;
            dfs(u+1);
            used[i]=false;
        }
    }
}
int main()
{
    cin>>target;
    dfs(0);//0是说数组第0个位置放了数,也就是第一个位置其实
    cout<<cnt<<endl;
    return 0;
}

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

using namespace std;
int target;

int cnt=0;
bool used[20];//生成全排列过程中标记是否使用
bool backup[20];//used的备份

bool check(int a,int c)
{
    int b=target*c-a*c;
    if(!b|!a|!c) return false;//如果c等于0的情况

    memcpy(backup,used,sizeof(used));
    while(b)
    {
        int t=b%10;
        b=b/10;
        if(!t||backup[t]) 
            return false;
        backup[t]=1;
    }
    for(int i=1;i<=9;i++)
    {
        if(!backup[i])     //检测是否都用过,应该每个数字都被利用
            return false;
    }
    return true;

}

void dfs_c(int x,int a,int c)
{
    if(x>=10) return;//剪枝操作
    if(check(a,c)) cnt++;//进入检验

    for(int i=1;i<=9;i++)
    {
        if(!used[i])
        {
            used[i]=1;
            dfs_c(x+1,a,c*10+i);
            used[i]=0;
        }
    }    
}

void dfs_a(int x,int a)
{
    if(a>=target) return;//剪枝操作:a的值已经大于了目标值
    if(a) dfs_c(x,a,0);//第三个位置代表c的大小
    for(int i=1;i<=9;i++)
    {
        if(!used[i])
        {
            used[i]=1;
            dfs_a(x+1,a*10+i);
            used[i]=0;
        }
    }

}
int main()
{
    cin>>target;
    dfs_a(0,0);//第一个位置记录使用了几个数字,第二个位置记录当前a的值
    cout<<cnt<<endl;
    return 0;
}

二,递归

95. 费解的开关 - AcWing题库

[外链图片转存中…(img-UsspnqQ5-1711015082491)]

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

using namespace std;

const int N=6;
char g[N][N];char backup[N][N];
int dx[N] = {-1, 0, 1, 0, 0}, dy[N] = {0, 1, 0, -1, 0};
void  turn_light(int x,int y)
{
    for(int i=0;i<5;i++)
    {
        int a=x+dx[i],b=y+dy[i];//我写代码的时候这里写错了,我用了x=x+dx[i]
        if(a<0||a>=5||b<0||b>=5) continue;

        backup[a][b] ^= 1;//异或,不同的时候就变成相反的数
    }

}
int main()
{
    int n;cin>>n;
    while(n--)
    {
        //把每行当成字符串进行输入
        for(int i=0;i<5;i++)    cin>>g[i];
        int res=10;
        //通过thefirstop进行第一行全部灯的熄灭还是亮着的枚举
        //通过第一行的情况,依此枚举剩下几行的操作
        for(int thefirstop=0;thefirstop<32;thefirstop++)
        {
            memcpy(backup,g,sizeof(g));
            int step=0;
            //处理第一行(1表示按了,0表示不按),为了数

            for(int i=0;i<5;i++)//使用这种方式得到第一行的哪一个列
            {
                if((thefirstop>>i)&1)//位移操作
                {
                    step++;
                    turn_light(0,i);//如果是操作是1那么就是说打开这个灯
                }
            }
            //处理2345行
            //i从0开始有 0 1 2 3 这么4行,最后由第五行进行大判断
            for(int i=0;i<4;i++)
            {
                for(int j=0;j<5;j++)
                {
                    if(backup[i][j]=='0')//前一行的灯是灭的
                    {
                        step++;
                        turn_light(i+1,j);//那么就按下对应位置的灯
                    }
                }
            }
            //默认:全亮的
            bool dark=false;
            //遍历最后一行的每一个列
            for(int j=0;j<5;j++)
            {
                if(backup[4][j]=='0')
                {
                    //如果最后一行有灭的那么这次灭灯,那么就失败
                    dark=true;
                    break;
                }
            }
            if(!dark) res=min(res,step);

        }
        if(res>6) res=-1;
        cout<<res<<endl;
    }

    return 0;
}

116. 飞行员兄弟 - AcWing题库

[外链图片转存中…(img-Jo7mprMK-1711015082492)]

1208. 翻硬币 - AcWing题库

[外链图片转存中…(img-MjS8etDC-1711015082492)]

#include<bits/stdc++.h>
using namespace std;
char origin[110],aim[110];
int res=0;
void turn_the_two(int i)
{
    origin[i]=(origin[i]=='*')?'o':'*';
    origin[i+1]=(origin[i+1]=='o')?'*':'o';
}
int main()
{
    scanf("%s",origin);scanf("%s",aim);
    int len=strlen(origin);
    for(int i=0;i<=len;i++)
    {
        if(origin[i]!=aim[i])
        {
            res++;
            turn_the_two(i);
        }
    }
    cout<<res<<endl;
    return 0;
}

三,前缀和

前缀和模板795. 前缀和 - AcWing题库

#include<iostream>
using namespace std;
int arr[100000],s[100000];
int main()
{
    int m,n;cin>>m>>n;
    //前缀和初始化
    for(int i=1;i<=m;i++)
    {
        cin>>arr[i];
        s[i]=s[i-1]+arr[i];
    }

    while(n--)
    {
        int l,r;cin>>l>>r;
        cout<<s[r]-s[l-1]<<endl;
    }
    return 0;
}

二维前缀和模板796. 子矩阵的和 - AcWing题库

#include<iostream>
using namespace std;
const int N=1010;
int arr[N][N],s[N][N];
int main()
{
    int n,m,q;cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            cin>>arr[i][j];
            s[i][j] = s[i][j-1]+s[i-1][j]-s[i-1][j-1]+arr[i][j];
        }
    while(q--)
    {
        int x1,y1,x2,y2;cin>>x1>>y1>>x2>>y2;
        cout<<s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]<<endl;//求子矩阵
    }
    return 0;
}

99. 激光炸弹 - AcWing题库

[外链图片转存中…(img-F2uSQTUe-1711015082492)]

#include<bits/stdc++.h>
using namespace std;
const int N=5010;

int n,m;
int s[N][N];

int main()
{
    int cnt,r;cin>>cnt>>r;
    //细节一:根据题库数据可以知道当r>=5001的时候,可以进行了全覆盖
    r=min(5001,r);
    //细节二:让m和n先行等于r,防止后面i-r出现数组越界,造成错误
    m=r,n=r;
    while(cnt--)
    {
        int x,y,w;cin>>x>>y>>w;
        //细节三:前缀和一律从1开始处理
        x++,y++;
        n=max(x,n),m=max(y,m);
        s[x][y]+=w;
    }
    //预处理前缀和
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
        }
    }
    //进行放置炸弹
    int res=0;
    for(int i=r;i<=n;i++)
        for(int j=r;j<=m;j++)
        {
            //这里其实根据s[x2,y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]
            //这里 x2=i y2=j  x1=i-r+1  y1=j-r+1
            //思考这里为什么要i-r+1呢,实际上是一个取整的思想,看题解的图
            res=max(res,s[i][j]-s[i-r][j]-s[i][j-r]+s[i-r][j-r]);
        }
    cout<<res<<endl;
    return 0;
}

1230. K倍区间 - AcWing题库

//前缀和+暴力搜索,我实在想不到别的思路了
#include<iostream>
using namespace std;
int arr[100010],s[100010];
int n,k;
int main()
{
    cin>>n>>k;int cnt=0;
    for(int i=1;i<=n;i++)
    {
        cin>>arr[i];
        s[i]=s[i-1]+arr[i];
    }
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
        {
            if((s[j]-s[i-1])%k==0)
            {
                cnt++;
            }
        }
    cout<<cnt<<endl;
}

//前缀和+暴力搜索,我实在想不到别的思路了
//如何进行优化:关键就是那两个循环
//能优化一部分是一部分
#include<iostream>
using namespace std;
long long arr[100010],s[100010],cnt[100010];
int n,k;
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>arr[i];
        s[i]=s[i-1]+arr[i];
    }
    long long res=0;
    cnt[0]=1;
    for(int i=1;i<=n;i++)
    {
        res+=cnt[s[i]%k];//找存储过的与s[i]%k得到的数相同的
        cnt[s[i]%k]++;
    }
    cout<<res<<endl;

    return 0;
}
//解释:
//1.使用(s[j]-s[i-1])%k==0同余定理
//2.两个整数a、b,若它们除以整数m所得的余数相等,则称a与b对模m同余或a同余于b模m

四,二分

[外链图片转存中…(img-Hw9VurL0-1711015082493)]

口诀:后r=mid +1

789. 数的范围 - AcWing题库

#include<iostream>
#include<vector>
using namespace std;
vector<int> tmp;
int SL(int l,int r,int query)
{
    while(l<r)
    {
        int mid=l+r>>1;
        if(tmp[mid]>=query) r=mid;
        else l=mid+1;
    }
    return l;
}
int SR(int l,int r,int query)
{
    while(l<r)
    {
        int mid=l+r+1>>1;
        if(tmp[mid]<=query) l=mid;
        else r=mid-1;
    }
    return l;
}
int main()
{
    int n,q;cin>>n>>q;
    for(int i=0;i<n;i++)
    {
        int a;cin>>a;
        tmp.push_back(a);
    }
    for(int i=0;i<q;i++)
    {
        //思考一下二段性,利用红绿线段进行思考
        //找sl就是找绿线段,也就是后面的那个
        //找sr就是找红线段,也就是红色的那一个
        int query;cin>>query;
        int sl=SL(0,tmp.size()-1,query),sr=SR(0,tmp.size()-1,query);
        if(tmp[sl]!=query) cout<<"-1 -1"<<endl;
        else cout<<sl<<" "<<sr<<endl;
    }
    return 0;
}

浮点数二分790. 数的三次方根 - AcWing题库

#include <iostream>

using namespace std;

int main()
{
    double x;
    cin >> x;
    double l = -100, r = 100;//要查找的区间(-10000到1000取三次根就得到了-100到100这个区间范围)
    while (r - l > 1e-8)
    {
        double mid =(l+r)/2;   
        if (mid * mid * mid >= x) r = mid;
        else l = mid;
    }

    printf("%.6lf\n", l);
    return 0;
}

AcWing 730. 机器人跳跃问题 - AcWing

暴力:

#include<bits/stdc++.h>
using namespace std;
//此暴力解法只对了6/13个数据
//后面打了个补丁全过了,哈哈哈
vector<int> tmp;
int main()
{
    int N;cin>>N;int back_N=N;
    while(back_N--)
    {
        int a;cin>>a;

        tmp.push_back(a);
    }
    //分析这个题的二段性质,没有分析出来开始暴力
    for(int a=0;a<99999;a++)
    {
        int flag=0;
        long long back_a=a;
        for(int i=0;i<N;i++)
        {
            back_a=2*back_a-tmp[i];
            if(back_a<0)
            {
                flag=1;
                break;
            }
            if(back_a>10e5)//暴力解法的补丁
            {
                break;
            }
        }
        if(flag==0)
        {
            cout<<a<<endl;
            break;
        }

    }

    return 0;
}

二分:

#include<bits/stdc++.h>
using namespace std;
//我们从解法一我写的这个暴力搜索来思考:for(int a=0;a<99999;a++)
//很明显我们可以用二分查找简化这个的复杂度
int H[100000];
int n;
bool check(int E)//判断当E可以的时候,且比这个E大的一定都可以,所以是绿色区间
{
    for(int i=0;i<n;i++)
    {
        E=2*E-H[i];
        if(E>=1e5) return true;
        if(E<0) return false;
    }
    return true;
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) scanf("%d",&H[i]);
    int l = 0, r = 1e5;
    while(l<r)
    {
        int mid=l+r>>1;
        if(check(mid)) r=mid;//if(绿) 后rmid
        else l=mid+1;
    }
    printf("%d\n",r);

    return 0;
}

1227. 分巧克力 - AcWing题

[外链图片转存中…(img-NljXUrbg-1711015082493)]

#include<bits/stdc++.h>
using namespace std;

const int N=100500;
int H[N],W[N];
int n,k;
bool check(int x)
{
    int res=0;
    for(int i=0;i<n;i++)
    {
        res += (W[i]/x)*(H[i]/x);
        if(res>=k) return true;
    }
    return false;
}
int main()
{
    cin>>n>>k;
    for(int i=0;i<n;i++)
    {
        cin>>H[i]>>W[i];
    }
    int l=0,r=1e5;
    while(l<r)//找红区间
    {
        int mid=(l+r+1)>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    printf("%d",r);
    return 0;
}

1221. 四平方和 - AcWing题库

暴力

#include<bits/stdc++.h>
using namespace std;
//没有思路:暴力,对了9个数据
int main()
{
    int N;cin>>N;
    for(int i1=0;i1*i1<=N;i1++)
    {
        for(int i2=i1;i2*i2<=N;i2++)
        {
            for(int i3=i2;i3*i3<=N;i3++)
            {
                for(int i4=i3;i4*i4<=N;i4++)
                {
                    if(pow(i1,2)+pow(i2,2)+pow(i3,2)+pow(i4,2)==N)
                    {
                        cout<<i1<<" "<<i2<<" "<<i3<<" "<<i4<<endl;
                        return 0;
                    }
                }   
            }
        }
    }
}

#include<cstdio>
#include<cmath>
using namespace std;
//没有思路:暴力,对了9个数据
int main()
{
    int N;scanf("%d",&N);
    for(int i1=0;i1*i1<=N;i1++)
    {
        for(int i2=i1;i1*i1+i2*i2<=N;i2++)
        {
            for(int i3=i2;i1*i1+i2*i2+i3*i3<=N;i3++)
            {
                int d=N-i1*i1-i2*i2-i3*i3;
                int i4=sqrt(d);
                if(i4*i4==d)
                {
                    printf("%d %d %d %d",i1,i2,i3,i4);
                    return 0;
                }
            }
        }
    }
}

哈希

最多枚举两个数:使用空间换时间的做法

先枚举c和d,并且将t= c^2 +d^2 存储起来

再枚举a和b,并且计算粗t= N -a^2 -b^2 如果t在前面出现过,那么就拿下了

剩下的问题就是如何保证c<=d呢?

方法一:二分法+结构体 方法二:哈希表法

另外如何保证b<=c呢?

枚举a和b时是从小到大枚举的,

所以如果出现答案满足b<c的情况时其实在b=c,c=b的时候就已经枚举到答案了

(就是假设当b=2,c=1是满足条件,但其实在枚举到b=1,c=2时就得到答案了)

所以b一定<=c

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef pair<int,int> PII;
unordered_map<int,PII> s;

int N;


int main()
{
    cin>>N;
    for(int c=0;c*c<=N;c++)
    {
        for(int d=c;c*c+d*d<=N;d++)
        {
            int t=c*c+d*d;
            if(s.count(t)==0) s[t]={c,d};
        }
    }
    
    for (int a = 0; a * a <= N; a ++ )
        for (int b = a; a * a + b * b <= N; b ++ )
        {
            int t=N-a*a-b*b;
            if(s.count(t))
            {
                printf("%d %d %d %d\n",a,b,s[t].first,s[t].second);
                return 0;
            }
        }
    return 0;
}

二分

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

int N,count1=0;

struct sum
{
    int s,c,d;
    bool operator < (const sum &t) const
    {
        if(s!=t.s) return s<t.s;
        if(c!=t.c) return c<t.c;
        if(d!=t.d) return d<t.d;
    }
    //const修饰成员函数表示该函数不会修改调用它的对象的成员变量.
    //而const Sum &t表示传入的参数t是一个常量引用,也就是说在这个函数中不能修改参数t所引用的对象.
}sum[2500010];
int main()
{
    cin>>N;
    for(int c=0;c*c<=N;c++)
    {
        for(int d=c;c*c+d*d<=N;d++)
        {
            sum[count1++]={c*c+d*d,c,d};
        }
    }
    sort(sum,sum+count1);//为了能将这东西存储并进行排序,我们来定义一个结构体

    for(int a=0;a*a<=N;a++)
        for(int b=a;a*a+b*b<=N;b++)
        {
            int t=N-a*a-b*b;
            int l=0,r=count1-1;
            while(l<r)//找满足的最左边,也就是if(绿)
            {
                int mid=(l+r)>>1;
                if(sum[mid].s>=t) r=mid;
                else l=mid+1;
            }
            if(sum[l].s==t)
            {
                printf("%d %d %d %d",a,b,sum[l].c,sum[l].d);
                return 0;
            }
        }

    return 0;
}

五,二分与前缀

1.一维差分

参考:AcWing 797. 差分 【c++详细题解】 - AcWing

#include<iostream>
using namespace std;
const int N=100010;
int a[N],b[N];
//举例子
//          0 1 2 2 1  2 1
//   差分数组:1 1 0 -1 1 -1
//在l=1 r=3 c=1的情况下进行处理
//   差分数组:2 1 0 -1 1 -1   //b[l] += c;
//            2 3 3  2 3  2   前三项本来是想要的,但是你会发现后三项也都加了该怎么办呢?
//   差分数组 2 1 0 -2 1 -1   //b[r + 1] -= c;
//            2 3 3  1 2  1   //这样仅仅加了前三项了
void insert(int l, int r, int c)
{
    b[l] += c;
    b[r + 1] -= c;
}
int main()
{
    int n,m;
    cin>>n>>m;
    //构建一维差分数组
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) b[i]=a[i]-a[i-1];
    while(m--)
    {
        int l,r,c;
        cin>>l>>r>>c;
        insert(l,r,c);
    }
    for(int i=1;i<=n;i++) b[i] += b[i - 1];//重新构建前缀和数组
    for(int i=1;i<=n;i++) cout<<b[i]<<" ";
    
    return 0;
}

解释一下构建一维差分数组这里其实可以用

**insert(i,i,a[i]);**b[i]=b[i]+a[i]; b[i+1]=b[i+1]-a[i];

           0 1 2 2 1  2 1

差分数组: 1 -1 b[1]=b[1]+a[1]; b[2]=b[2]-a[1];

              1 1 -2        **b\[2\]=b\[2\]+a\[2\]; b\[3\]=b\[3\]-a\[2\];**

              1 1 0

2.二维差分

image

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N][N],b[N][N];
void insert(int x1,int y1,int x2,int y2,int c)
{
    b[x1][y1]+=c;
    b[x1][y2+1]-=c;
    b[x2+1][y1]-=c;
    b[x2+1][y2+1]+=c;
}
int main()
{
    int n,m,q;cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>a[i][j];
        }
    }
    //构建二维差分数组
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            insert(i,j,i,j,a[i][j]);
        }
    }
    //进行询问
    while(q--)
    {
        int x1,y1,x2,y2,c;cin>>x1>>y1>>x2>>y2>>c;
        insert(x1, y1, x2, y2, c);
    }
    //结束
    //构建二维前缀和数组
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cout<<b[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

六,dp动态分析法

一.闫氏分析法:

1.核心思想:所有的DP问题,本质上都是有限集中的最值问题

2.阶段:状态表示和状态计算

状态表示:化零为整
             **集合**:F(i) 表示什么集合

    **属性**:F(i) 存的数与集合的关系:如 max,min,count,sum 等
状态计算:化整为零
                   集合的划分:划分为若干个不重不漏的子集

划分的依据:找最后一个不同点

二,普通dp分析法

  1. 明确dp数组的含义

  2. 递推公式

  3. 初始化dp数组

  4. 打印dp数组

三,典型例题:

1.2. 01背包问题 - AcWing题库(01背包)

普通分析:
  1. 明确dp数组的含义 dp[i][j]:

[0,i]物品任取,放进容量为j的背包中

  1. 递推公式

不放物品i dp[i-1][j]

放物品i dp[i-1][j-v[i]]+w[i]

dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])

  1. 初始化dp数组

  2. 打印dp数组

闫氏dp分析法:

[外链图片转存中…(img-CrZasfFB-1711015082494)]

代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1005;
int dp[MAXN][MAXN];//dp方程,前面代表物品数量,后面代表价值.
int v[MAXN],w[MAXN];

int main()
{
    int n,m;cin>>n>>m;//读入物品数量和背包容积
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            //装不进第i件产品时,价值等于前i-1个产品
            if(v[i]>j) 
            {
                dp[i][j]=dp[i-1][j];
            }
            //能装进的时候
            else
            {
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);   
            }
        }
    cout<<dp[n][m]<<endl;;

    return 0;
}

作者:namespacelakepause
链接:https://www.acwing.com/activity/content/code/content/7959816/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

优化参考文章:AcWing 2. 01背包问题(状态转移方程讲解) - AcWing

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1005;
int dp[MAXN];//dp方程代表价值.
int v[MAXN],w[MAXN];

int main()
{
    int n,m;cin>>n>>m;//读入物品数量和背包容积
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=m;j>=0;j--)
        {
            /*//装不进第i件产品时,价值等于前i-1个产品
            if(v[i]>j) 
            {
                dp[j]=dp[j];
            }*/
            //能装进的时候
            if(v[i]<=j)
            {
                dp[j]=max(dp[j],dp[j-v[i]]+w[i]);   
            }
        }    
    }

    cout<<dp[m]<<endl;;
    
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
const int MAXN=1005;
int dp[MAXN];//dp方程代表价值.

int main()
{
    int n,m;cin>>n>>m;//读入物品数量和背包容积
    for(int i=1;i<=n;i++)
    {
        int v,w;cin>>v>>w;
        for(int j=m;j>=v;j--)
        {
            dp[j]=max(dp[j],dp[j-v]+w);   
        }    
    }
    cout<<dp[m]<<endl;;
    
    return 0;
}

2.3. 完全背包问题 - AcWing题库

dp分析法

[外链图片转存中…(img-CiJAZduS-1711015082494)]

代码
#include<bits/stdc++.h>
using namespace std;

int dp[1005][1005];
int v[1005],w[1005];

int main()
{
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
        }
    }
    cout<<dp[n][m]<<endl;
    return 0;
}

3.282. 石子合并 - AcWing题库(区间dp)

image

#include<bits/stdc++.h>
using namespace std;

int dp[1005][1005];
int s[1005];
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        s[i]=s[i]+s[i-1];//前缀和数组
    }
    for(int len=2;len<=n;len++)//枚举区间的长度
    {
        for(int i=1;i+len-1<=n;i++)
        {
            int l=i,r=i+len-1;
            dp[l][r]=1e8;
            for(int k=l;k<r;k++)
            {
                dp[l][r]=min(dp[l][k]+dp[k+1][r]+s[r]-s[l-1],dp[l][r]);
            }
        }
    }
    cout<<dp[1][n]<<endl;
    return 0;
}

4.3510. 最长公共子序列 - AcWing题库(线性dp)

[外链图片转存中…(img-Iqm5o9pS-1711015082495)]

#include<bits/stdc++.h>
using namespace std;

int dp[1005][1005];
char a[1005],b[1005];

int main()
{
    //数据读入
    int m,n;cin>>m>>n;
    for(int i=1;i<=m;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<=n;i++)
    {
        cin>>b[i];
    }
    //进行动规
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
            if(a[i]==b[j]) 
            {
                dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
            }
            
        }
    cout<<dp[m][n]<<endl;
    return 0;
}

5.1015. 摘花生 - AcWing题库(01背包)

[外链图片转存中…(img-KLspMdiq-1711015082495)]

#include<bits/stdc++.h>
using namespace std;

int w[105][105];
int dp[105][105];
int main()
{
    int n;cin>>n;
    while(n--)
    {
        int r,c;cin>>r>>c;
        for(int i=1;i<=r;i++)
            for(int j=1;j<=c;j++)
                cin>>w[i][j];
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=r;i++)
            for(int j=1;j<=c;j++)
            {
                dp[i][j]=max(dp[i-1][j]+w[i][j],dp[i][j-1]+w[i][j]);
            }
        cout<<dp[r][c]<<endl;
    }
    
    return 0;
}

6.895. 最长上升子序列 - AcWing题库(线性dp)

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

int a[1005];
int dp[1005];

int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)  
        cin>>a[i];
    int res=0;
    for(int i=1;i<=n;i++)
    {
        dp[i]=1;
        for(int j=1;j<i;j++)
        {
            if(a[j]<a[i])
            {
                dp[i]=max(dp[i],dp[j]+1);
            }
        }
        res=max(res,dp[i]);
    }
    cout<<res<<endl;
    
    return 0;
}

image

dp[i]是所有以a[i]为结尾的严格单调上升子序列

划分依据:最后的一步的不同,

第一类:倒数第二个数是a[1],倒数第二个数是a[k],倒数第二个数是a[i-1]

第二类:只包含a[i],倒数第二个数为空

7.1214. 波动数列 - AcWing题库

参考更优的题解:AcWing 1214. 波动数列 - AcWing

[外链图片转存中…(img-AvGdkI1w-1711015082495)]

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1010, MOD = 100000007;

int f[N][N];//f[i][j]代表取前i个数,余数为j的方案数量

int get_mod(int a,int b)
{
    return (a % b + b) % b;//(a % b + b) % b
}
int main()
{
    int n,s,a,b;cin>>n>>s>>a>>b;
    f[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<n;j++)//余数只能从0取到n-1
        {
            f[i][j]=(f[i-1][get_mod(j-a*i,n)]+f[i-1][get_mod(j+b*i,n)])%MOD;
        }
    }
    cout<<f[n-1][get_mod(s,n)]<<endl;
    //为什么要减一,因为一开始从i=1开始进行遍历的
    return 0;
}

8.1212. 地宫取宝 - AcWing题库

[外链图片转存中…(img-KZ9YvfL0-1711015082496)]

其中取为什么要划分:上一个物品到底为多少数值

#include<iostream>
#include<algorithm>
#include<cstdio>

using namespace std;
const int MOD=1000000007;
int w[55][55];
int f[55][55][14][14];
// f[i][j][cnt][k] 表示:在 (i, j) 这个点,拿了 cnt 个物品,这些物品中价值最大的是 k
int main()
{
    int n,m,k;cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>w[i][j];
            w[i][j]++;
        }
    }
    //进行初始化
    f[1][1][1][w[1][1]]=1;
    f[1][1][0][0]=1;
    //进行遍历
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(i==1&&j==1) continue;
            for(int u=0;u<=k;u++)
            {
                for(int v=0;v<=13;v++)
                {
                    int &val=f[i][j][u][v];
                    //不取的情况
                    val=(val+f[i-1][j][u][v])%MOD;
                    val=(val+f[i][j-1][u][v])%MOD;
                    //取的情况
                    if(u>0 && v==w[i][j])
                    {
                        for(int c=0;c<v;c++)
                        {
                           // 取了一个所以之前的状态为u-1 val=(val+f[i-1][j][u-1][c])%MOD;
                            val=(val+f[i][j-1][u-1][c])%MOD;
                        }
                    }
                }
            }
        }
    }
    int res=0;
    for (int i = 0; i <= 13; i ++ ) res = (res + f[n][m][k][i]) % MOD;
    cout<<res<<endl;
    return 0;
}

七,模拟、枚举

1.1210. 连号区间数 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
int a[10010];
int INF=100000;

int main()
{
    int n;cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    int res=0;
    for(int i=0;i<n;i++)
    {
        int maxinf=-INF,mininf=INF;
        for(int j=i;j<n;j++)
        {
            maxinf=max(maxinf,a[j]);
            mininf=min(mininf,a[j]);
            if(maxinf-mininf==j-i) res++;
        }
    }
    cout<<res<<endl;
    return 0;
}

2.AcWing 1236. 递增三元组 - AcWing

#include<bits/stdc++.h>
using namespace std;
// 利用前缀和优化暴力的n^2为n
//利用二分可以优化到nlogn
const int N=1e5+10;
int a[N],b[N],c[N];
int as[N];//as[i] 比i小的a中的数的个数
int cs[N];//bs[i] 比i大的b中的数的个数
int cnt[N],s[N];//s[i] 个数的前缀和
int main()
{
    //进行读入
    int n;cin>>n;
    for(int i=0;i<n;i++) cin>>a[i],a[i]++;
    for(int i=0;i<n;i++) cin>>b[i],b[i]++;
    for(int i=0;i<n;i++) cin>>c[i],c[i]++;

    //求as[i]
    for(int i=0;i<n;i++) cnt[a[i]]++;
    for(int i=1;i<N;i++) s[i]=s[i-1]+cnt[i];//关于数量的前缀和
    for(int i=0;i<N;i++) as[i]=s[b[i]-1];
    //求cs[i]
    memset(s,0,sizeof(s));
    memset(cnt,0,sizeof(cnt));
    for(int i=0;i<n;i++) cnt[c[i]]++;
    for(int i=1;i<N;i++) s[i]=s[i-1]+cnt[i];//关于数量的前缀和
    for(int i=0;i<N;i++) cs[i]=s[N-1]-s[b[i]];
    //进行枚举每个b[i]
    long long res=0;
    for(int i=0;i<n;i++)
    {
        res+=(long long)as[i]*cs[i];
    }
    cout<<res<<endl;
    return 0;
}

手写二分查找

#include<bits/stdc++.h>
using namespace std;
//利用二分可以优化到nlogn
const int N=1e5+10;
typedef long long LL;
int a[N],b[N],c[N];
int BSL(int l,int r,int midd)
{
    while(l<r)
    {
        int mid=(l+r+1)>>1;
        if(a[mid]<midd) l=mid;
        else r=mid-1;
    }
    return l;
}
int BSR(int l,int r,int midd)
{
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(c[mid]>midd) r=mid;
        else l=mid+1;
    }
    return l;
}
int main()
{
    //进行读入
    int n;cin>>n;
    for(int i=0;i<n;i++) cin>>a[i],a[i]++;
    for(int i=0;i<n;i++) cin>>b[i],b[i]++;
    for(int i=0;i<n;i++) cin>>c[i],c[i]++;
    //进行排序
    sort(a,a+n),sort(b,b+n),sort(c,c+n);
    LL sum=0;
    for(int i=0;i<n;i++)
    {
        //手写二分查找
        //右边最后一个
        LL SL=BSL(0,n-1,b[i]);
        if (a[SL] >= b[i])   //如果未找到小于b[i]的数,将x标记为-1,后续计算时 x+1==0
        {
            SL = -1;
        }
        //左边第一个
        LL SR=BSR(0,n-1,b[i]);
        if (c[SR] <= b[i])   //如果未找到大于b[i]的数,将y标记为n,后续计算时 n-y==0;
        {
            SR = n;
        }
        sum+=(SL + 1)*(n - SR);
    }
    cout<<sum<<endl;
    return 0;
}

STL算法

#include<bits/stdc++.h>
using namespace std;
//利用二分可以优化到nlogn
const int N=1e5+10;
typedef long long LL;
int a[N],b[N],c[N];

int main()
{
    //进行读入
    int n;cin>>n;
    for(int i=0;i<n;i++) cin>>a[i],a[i]++;
    for(int i=0;i<n;i++) cin>>b[i],b[i]++;
    for(int i=0;i<n;i++) cin>>c[i],c[i]++;
    //进行排序
    sort(a,a+n),sort(b,b+n),sort(c,c+n);
    LL sum=0;
    for(int i=0;i<n;i++)
    {
        LL sl=lower_bound(a,a+n,b[i])-a;
        LL sr=n-(upper_bound(c,c+n,b[i])-c);
        sum+=sl*sr;
    }
    cout<<sum<<endl;
    return 0;
}

3.1245. 特别数的和 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
int res=0;
int main()
{
    int n;cin>>n;
    int res=0;
    for(int i=1;i<=n;i++)
    {
        int x=i;
        while(x)
        {
            int t=x%10;//取出最低位数
            x /=10;//剥除个位数
            if(t==2||t==0||t==1||t==9)
            {
                res+=i;
                break;
            }
        }
    }
    cout<<res<<endl;
    
    return 0;
}

4.1204. 错误票据 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
//关键是如何进行输入
//输入方式一
const int N=10010;
int a[N];
int main()
{
    int n=0;
    int cnt;cin>>cnt;
    string line;
    getline(cin,line);
    while(cnt--)
    {
        getline(cin,line);
        stringstream ssin(line);
        while(ssin>>a[n]) n++;
    }
    sort(a,a+n);
    int res1,res2;
    for(int i=1;i<n;i++)
    {
        if(a[i]==a[i-1]) res1=a[i];
        else if(a[i]>a[i-1]+1) res2=a[i]-1;
    }
    cout<<res2<<" "<<res1<<endl;
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
//关键是如何进行输入
//输入方式二,
const int N=100100,INF=0x3f3f3f3f;
int a[N],hash_a[N];
int main()
{
    int n;cin>>n;
    int minv=INF,maxv=-INF;
    int tp;
    while(cin >> tp)//直接读到文件尾部停止
    {
        if(tp < minv)   minv = tp;
        if(tp > maxv)   maxv = tp;
        hash_a[tp] ++;
    }
    int res1,res2;
    for(int i=minv;i<=maxv;i++)
    {
        if(hash_a[i]==0) res1=i;
        if(hash_a[i]==2) res2=i;
    }
    cout<<res1<<" "<<res2<<endl;
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
//关键是如何进行输入
//输入方式二,
const int N=100100,INF=0x3f3f3f3f;
int a[N],hash_a[N];
int main()
{
    int n;cin>>n;
    int minv=INF,maxv=-INF;
    int tp;
    while(scanf("%d",&tp)!=EOF)//直接读到文件尾部停止
    while(cin>>tp)
    {
        if(tp < minv)   minv = tp;
        if(tp > maxv)   maxv = tp;
        hash_a[tp] ++;
    }
    int res1,res2;
    for(int i=minv;i<=maxv;i++)
    {
        if(hash_a[i]==0) res1=i;
        if(hash_a[i]==2) res2=i;
    }
    cout<<res1<<" "<<res2<<endl;
    return 0;
}

5.466. 回文日期 - AcWing题库

#include<bits/stdc++.h>
using namespace std;

int day_[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
bool check_valid(int date)
{
    int year=date/10000;
    int month=(date%10000)/100;
    int day=date%100;
    
    if(month==0||month>12) return false;
    if (day == 0 || month != 2 && day > day_[month]) return false;
    if(month==2)
    {
        int leap = (year % 100 !=0 && year % 4 == 0) || (year % 400 == 0);//这个就是闰年
        if (day > 28 + leap) return false;
    }
    return true;
}
int main()
{
    int date1,date2;
    cin>>date1>>date2;
    
    int res=0;
    for(int i=1000;i<=9999;i++)
    {
        //下面两行代码是一种制造回文数的方法
        //例如:date=1002  x=1002
        //     10022     x=100
        //     100220    x=10
        //     1002200   x=1
        //     10022001  x=0
        int date = i, x = i;
        for (int j = 0; j < 4; j ++ ) date = date * 10 + x % 10, x /= 10;
        if(date<=date2 && date>=date1 &&check_valid(date)) res++;
    }
    cout<<res<<endl;
    return 0;
}

6.1219. 移动距离 - AcWing题库

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    int v,n,m;cin>>v>>m>>n;
    int n_x,m_x,n_y,m_y;
    if(n%v==0)
    {
        n_x=n/v ;
        if(n_x%2==0) n_y=1;
        else         n_y=v;
    }
    else
    {
        n_x=n/v + 1;   
        if(n_x%2==0) n_y=v-n%v +1;
        else         n_y=n%v;
    }
    
    if(m%v==0)
    {
        m_x=m/v ;
        if(m_x%2==0) m_y=1;
        else         m_y=v;
    }
    else
    {
        m_x=m/v + 1;   
        if(m_x%2==0) m_y=v-m%v +1;
        else         m_y=m%v;
    }
    cout<<abs(m_y-n_y)+abs(m_x-n_x)<<endl;
    
    return 0;
}

[外链图片转存中…(img-KO5UWvST-1711015082496)]

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

using namespace std;

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

    int x1 = m / w, x2 = n / w;
    int y1 = m % w, y2 = n % w;
    if (x1 % 2) y1 = w - 1 - y1;
    if (x2 % 2) y2 = w - 1 - y2;

    cout << abs(x1 - x2) + abs(y1 - y2) << endl;

    return 0;
}

7.AcWing 1229. 日期问题 - AcWing

#include<bits/stdc++.h>
using namespace std;
int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

bool check_valid(int year, int month, int day)
{
    if (month == 0 || month > 12) return false;
    if (day == 0) return false;
    if (month != 2)
    {
        if (day > days[month]) return false;
    }
    else
    {
        int leap = year % 100 && year % 4 == 0 || year % 400 == 0;
        if (day > 28 + leap) return false;
    }

    return true;
}
int main()
{
    //枚举 年/月/日   月/日/年  日/月/年
    //判断合法
    //判断是否在1960年1月1日至2059年12月31日中
    int a,b,c;
    scanf("%d/%d/%d",&a,&b,&c);
    for(int date=19600101;date<=20591231;date++)
    {
        int year=date/10000,month=date / 100 % 100,day=date%100;
        if(check_valid(year,month,day))
        {
            if((year%100==a && month ==b &&day==c         )||//年月日
               (month==a    && day==b    &&year%100==c    )||//月日年
               (day==a    && month==b    &&year%100==c    ))//日月年
            {
                printf("%d-%02d-%02d\n",year,month,day);
            }
        }
    }
    return 0;
}

8.1231. 航班时间 - AcWing题库

image

往东飞 :到达时间 - (起飞时间 - 时差)= 到达时间1 - 起飞时间1 + 时差

往西飞: 到达时间 - (起飞时间 + 时差)= 到达时间2 - 起飞时间2 - 时差

两次时间相加 = 到达时间1 - 起飞时间1 + 到达时间2 - 起飞时间2

所以一次飞行时间为: 两次的时间差 / 2

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
//获取秒数
int get_time()
{
    int h1,m1,s1,h2,m2,s2,d=0;
    scanf("%d:%d:%d %d:%d:%d (%d)",&h1,&m1,&s1,&h2,&m2,&s2,&d);
    int time =(d*24*60*60+h2*3600+m2*60+s2)-(h1*3600+m1*60+s1);
    return time;
}

int main()
{
    int n;cin>>n;
    for(int i=0;i<n;i++)
    {
        int time1=get_time();
        int time2=get_time();
        int time=(time1+time2)/2;
        printf("%02d:%02d:%02d",time/3600,time/60 % 60,time %60);
        cout<<endl;
    }
    
    return 0;
}

:::
sscanf(line.c_str(), “%d:%d:%d %d:%d:%d (+%d)”, &h1, &m1, &s1, &h2, &m2, &s2, &d);
:::

将line中的每个字符,按照%d:%d:%d %d:%d:%d (+%d)赋值给 &h1, &m1, &s1, &h2, &m2, &s2, &d

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

using namespace std;

int get_seconds(int h, int m, int s)
{
    return h * 3600 + m * 60 + s;
}

int get_time()
{
    string line;
    getline(cin, line);

    if (line.back() != ')') line += " (+0)";

    int h1, m1, s1, h2, m2, s2, d;
    sscanf(line.c_str(), "%d:%d:%d %d:%d:%d (+%d)", &h1, &m1, &s1, &h2, &m2, &s2, &d);

    return get_seconds(h2, m2, s2) - get_seconds(h1, m1, s1) + d * 24 * 3600;
}

int main()
{
    int n;
    scanf("%d", &n);
    string line;
    getline(cin, line);     // 忽略掉第一行的回车
    while (n -- )
    {
        int time = (get_time() + get_time()) / 2;
        int hour = time / 3600, minute = time % 3600 / 60, second = time % 60;
        printf("%02d:%02d:%02d\n", hour, minute, second);
    }

    return 0;
}

9.1241. 外卖店优先级 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
int shop[10010][10010];//shop[i][j]  i商店在j时刻的订单
bool state_[10010];//判断i商店是否处在优先缓存中
int score_[10010];//i商店此时的优先级为多少
int main()
{
    //数据输入
    int n,m,t;//给定t时刻内的m条订单信息,维护有n家外卖店,t时刻时有多少外卖店在优先缓存中
    cin>>n>>m>>t;
    for(int i=0;i<m;i++)
    {
        int x,y;cin>>x>>y;//x时刻 y商店
        shop[y][x]++;
    }
    //处理
    for(int j=1;j<=t;j++)//时间
    {
        //遍历每个商店
        for(int i=1;i<=n;i++)
        {
            if(shop[i][j]) score_[i] +=2*shop[i][j];
            else if(!shop[i][j])
            {
                if(score_[i]>=1) score_[i]--;
            }
            if(score_[i]>5)   state_[i]=true;
            if(score_[i]<=3)  state_[i]=false;
        }
    }
    int res=0;
    for(int i=1;i<=n;i++)
    {
        res+=state_[i];
    }
    cout<<res<<endl;
    return 0;
}

优化的参考解法:

AcWing 1241. 外卖店优先级(暴力+优化 详细分析) - AcWing

按我自己理解该怎么写呢?

暴力写法中遍历每个时间,然后遍历每个商店,

1.我们会发现某个时间,很多商店是没有接单量的,该怎么省去这方面的判断呢?

所以我们采用压缩的方法,具体该怎么压缩呢?那就是用个数组表示第i个店铺上次有订单的时间,那么计算下次的时候直接-(时间差),当没有下次的时候直接减去最后要的时刻

2.我们对代码进行解析:

1.进行排序时为了,更好的集中订单比如第一时刻第一家店铺收到了订单有好多次,那么就进行排序

 2.这段代码完成了对相同的order的处理

    for(int i=0;i<m;)
    {
        int j=i;
        while(j<m&&order[i]==order[j])//找到相同时间相同商家的订单
            j++;
        int t=order[i].x,id=order[i].y,cnt=j-i;//同一时间同意商家的定点数量
        i=j;

3.进行处理此时的score也就是优先级

 //处理t时刻之前的信息
        score_[id]=score_[id]-(t-last[id]-1);//减去中间的没有订单的数量t-last[id]-1;
        if(score_[id]<0) score_[id]=0;
        if(score_[id]<=3) state_[id]=false;
  //处理t时刻之时的情况
        score_[id]=score_[id]+cnt*2;
        if(score_[id]>5) state_[id]=true;
        last[id]=t;

4.最后对没有下一个订单的在最后事件之前的进行处理,采用(t-last[i])来处理

    for (int i = 1; i <= n; i++) {
        if (last[i] < t) {
            score_[i] =score_[i]-( t - last[i]); // 此时不用-1,因为T时刻没有订单
            if (score_[i] <= 3) state_[i] = false;
        }
    }

完美解法

#include<bits/stdc++.h>
using namespace std;
int last[100010];//表示第i个店铺上一次有订单的时刻
bool state_[100010];//判断i商店是否处在优先缓存中
int score_[100010];//i商店此时的优先级为多少
typedef pair<int,int> PII;
PII order[100010];
#define x first
#define y second
int main()
{
    //数据输入
    int n,m,t;//给定t时刻内的m条订单信息,维护有n家外卖店,t时刻时有多少外卖店在优先缓存中
    cin>>n>>m>>t;
    for(int i=0;i<m;i++)
    {
        cin>>order[i].x>>order[i].y;
    }
    sort(order,order+m);
    //优先按照x也就是时间进行排列
    //比如题目里的
    //1 1    //1 1
    //5 2    //1 2
    //3 1    //2 1
    //6 2    //3 1
    //2 1    //5 1
    //6 2    //6 1
    //1 2    //6 2
    //处理
    for(int i=0;i<m;)
    {
        int j=i;
        while(j<m&&order[i]==order[j])//找到相同时间相同商家的订单
            j++;
        int t=order[i].x,id=order[i].y,cnt=j-i;//同一时间同意商家的定点数量
        i=j;
        
        //处理t时刻之前的信息
        score_[id]=score_[id]-(t-last[id]-1);//减去中间的没有订单的数量t-last[id]-1;
        if(score_[id]<0) score_[id]=0;
        if(score_[id]<=3) state_[id]=false;
        //处理t时刻之时的情况
        score_[id]=score_[id]+cnt*2;
        if(score_[id]>5) state_[id]=true;
        last[id]=t;
    }
    //处理边界,有的是每一个店铺最后一段时间可能都没有订单,我们还要算一下最后一段时间,last[id] ~ T之间有多长时间没有卖东西
    for (int i = 1; i <= n; i++) {
        if (last[i] < t) {
            score_[i] =score_[i]-( t - last[i]); // 此时不用-1,因为T时刻没有订单
            if (score_[i] <= 3) state_[i] = false;
        }
    }
    int res=0;
    for (int i = 1; i <= n; i ++ ) res +=  state_[i];
    cout<<res<<endl;
    return 0;
}

10.756. 蛇形矩阵 - AcWing题库

#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
int arr[100][100];
int main()
{
    int n,m;//初始化n为行,m为列;
    cin>>n>>m;
    //偏移量技巧的使用
    int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
    for(int x=0,y=0,d=0,k=1;k<=n*m;k++)//初始化坐标x和y,初始化偏移方向为向右
    {
        arr[x][y]=k;
        int a=x+dx[d],b=y+dy[d];
        if(a<0||a>=n||b<0||b>=m||arr[a][b])//当超出边界或者说数组里面已经有值的时候
        {
            d=(d+1)%4;//撞头了就改变转向
            //上面的a和b作废,用下面的a和b
            a=x+dx[d],b=y+dy[d];
        }
        x=a,y=b;
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            cout<<arr[i][j]<<" ";
        }
        cout<<endl;
    }

    return 0;
}

11.1237. 螺旋折线 - AcWing题库

[外链图片转存中…(img-o7SkGqsa-1711015082496)]

2.找规律做法:

参考资料:AcWing 1237. 螺旋折线 C++ - AcWing(没参考)
ac代码:我自己写的线性规划做法

image

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int x_1,y_1;cin>>x_1>>y_1;
    if(x_1+y_1==0)
    {
        if(x_1>0) cout<<(2*x_1+1)*2*x_1<<endl;
        if(x_1<0) cout<<(2*abs(x_1)-1)*2*abs(x_1)<<endl;
    }
    if(x_1==y_1) cout<<pow(2*x_1,2)<<endl;
    if(x_1==y_1-1) cout<<pow(abs(x_1)+abs(y_1),2)<<endl;
    //通过这四个角找四个边,利用线性规划
    if(y_1>x_1&&y_1>-x_1)//在上方
    {
        ll step=x_1+y_1;
        ll distance_0=(ll)(2*abs(y_1)-1)*2*abs(y_1);
        ll distance=(ll)distance_0+step;
        //cout<<distance_0<<endl;
        cout<<distance<<endl;
    }
    else if(y_1<x_1&&y_1>-x_1)//在右方
    {
        ll step=x_1-y_1;
        ll distance_0=(ll)2*x_1*2*x_1;
        ll distance=(ll)distance_0+step;
        //cout<<distance_0<<endl;
        //cout<<step<<endl;
        cout<<distance<<endl;
    }
    else if(y_1<x_1+1&&y_1<-x_1)//在下方
    {
        ll step=-x_1-y_1;
        ll distance_0=(ll)(2*abs(y_1)+1)*2*abs(y_1);
        ll distance=distance_0+step;
        cout<<distance<<endl;
    }
    else if(y_1>x_1+1&&y_1<-x_1)//在左方
    {
        ll step=-x_1-y_1;
        ll distance_0=(ll)(2*abs(x_1)-1)*2*abs(x_1);//往后数
        ll distance=distance_0-step;
        cout<<distance<<endl;
    }
    return 0;
}

3.题解的优雅做法:

参考:AcWing 1237. 10行代码优雅解决螺旋折线所有情况 - AcWing
解析:[外链图片转存中…(img-pm74Jpps-1711015082497)]
ac代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
    long long x,y; cin>>x>>y;
    long long k = max(abs(x), abs(y));
    if(x >= y) cout<<4 * k * k + abs(x - k) + abs(y - k)<<endl;
    else cout<<4 * k * k - abs(x - k) - abs(y - k)<<endl;
    return 0;
}

八,排列

1.模板:归并排序787. 归并排序 - AcWing题库

#include<bits/stdc++.h>  
using namespace std;
const int N=100050;
int tmp[N],q[N];
void merge_sort(int q[],int l,int r)
{
    //1.出局规则
    if(l>=r) return ;
    //2.选择中间点来划分区间
    int mid=l+r>>1;
    //3.分组递归'
    merge_sort(q,l,mid);
    merge_sort(q,mid+1,r);
    //4.排序1+3+2    +    复刻
    int k=0,i=l,j=mid+1;
    while(i<=mid &&j<=r)
        if(q[i]<q[j]) tmp[k++] = q[i++];
        else tmp[k++]=q[j++];
    while(i<=mid)   tmp[k++]=q[i++];
    while(j<=r)     tmp[k++]=q[j++];

    for(int i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];
}
int main()
{
    int n;cin>>n;
    for(int i=0;i<n;i++) cin>>q[i];
    merge_sort(q,0,n-1);
    for(int i=0;i<n;i++) cout<<q[i]<<" ";
    return 0;
}

2.788. 逆序对的数量 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
const int N=1000060;
int q[N],tmp[N];
typedef long long lld;

lld merge_sort(int q[],int l,int r)
{
    if(l>=r) return 0;
    int mid=(l+r)>>1;
    lld res_1=merge_sort(q,l,mid)+merge_sort(q,mid+1,r);

    int k=0,i=l,j=mid+1;
    while(i<=mid && j<=r)
    {
        if(q[i]<=q[j]) tmp[k++]=q[i++];
        else           
        {
            res_1+=mid-i+1;
            tmp[k++]=q[j++];
        }
    }
    while(i<=mid) tmp[k++]=q[i++];
    while(j<=r)   tmp[k++]=q[j++];
    for (int i = l, j = 0; i <= r; i ++, j ++) q[i] = tmp[j];
    return res_1;
}

int main()
{
    int n;cin>>n;
    for(int i=0;i<n;i++) cin>>q[i];
    cout<<merge_sort(q,0,n-1)<<endl;
    return 0;
}

九,双指针算法

1.1238. 日志统计 - AcWing题库

1.通过7/15的一个暴力解法

[外链图片转存中…(img-RWIMKZao-1711015082497)]

//暴力解法
#include<bits/stdc++.h>
using namespace std;
#define ts first
#define id second
typedef pair<int,int> PII;
const int N=100100;
PII blogs[N];
int cnt[N];
bool state[N];
int main()
{
    int n,d,k;cin>>n>>d>>k;
    int max_t=0;
    for(int i=1;i<=n;i++) 
    {
        cin>>blogs[i].ts>>blogs[i].id;
        max_t=max(max_t,blogs[i].ts);
    }
    //暴力遍历时间
    for(int t=0;t<=max_t;t++)
    {
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)
        {
            int t_now=blogs[i].ts;
            int id_now=blogs[i].id;
            if(t_now>=t&&t_now<t+d)
            {
                cnt[id_now]++;
            }
            if(cnt[id_now]>=k)
            {
                state[id_now]=true;
            }
        }
    }
    int res=0;
    for(int i=0;i<=1e5;i++)
    {
        if(state[i])
        {
            cout<<i<<endl;
        }
    }
    return 0;
}

2.双指针滑动窗口解法

//滑动窗口
#include<bits/stdc++.h>
using namespace std;
#define ts first
#define id second
typedef pair<int,int> PII;
const int N=100100;
PII blogs[N];
int cnt[N];
bool state[N];
int main()
{
    int n,d,k;cin>>n>>d>>k;
    for(int i=0;i<n;i++)
    {
        cin>>blogs[i].ts>>blogs[i].id;
    }
    //进行排序
    sort(blogs,blogs+n);
    //排序结果是优先时间,再次id
    for(int right=0,left=0;right<n;right++)
    {
        //进数
        int id_=blogs[right].id;
        cnt[id_]++;
        //出窗口
        while(blogs[right].ts-blogs[left].ts>=d)
        {
            cnt[blogs[left].id]--;
            left++;
        }
        //判断
        if(cnt[id_]>=k)
        {
            state[id_]=true;
        }
    }
    for(int i=0;i <= 100000;i++)
    {
        if(state[i])
        {
            cout<<i<<endl;
        }
    }
    return 0;
}

2.1240. 完全二叉树的权值 - AcWing题库

[外链图片转存中…(img-k8dQifBH-1711015082497)]

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N];
int depth;
int main()
{
    int n;cin>>n;
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    long long max_=-1e18;
    for(int d=1,i=1;i<=n;i*=2,d++)
    {
        long long s=0;
        for(int j=i;j < i+ ( 1 << (d-1)) && j<=n ; j++)
        {
            s+=a[j];
        }
        if (s > max_)
        {
            max_ = s;
            depth = d;
        }
    }
    cout<< depth<<endl;
    return 0;
}

十,BFS

bfs解题流程:

  1. 初始状态入队

  2. while(队列不是空的)

2.1 取出队头元素放入t,并弹出队头

2.2 for(扩展队列) 经过判重,将新节点插入队尾

1.1101. 献给阿尔吉侬的花束 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
#define x first
#define y second
const int N=210;
int n,m;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
char g[N][N];
int d[N][N];

int bfs(PII start,PII end)
{
    memset(d,-1,sizeof(d));
    //1.初始状态入队
    queue<PII> q;
    q.push(start);
    d[start.x][start.y]=0;
    //2.while(队列不是空)
    while(q.size())
    {
        //  2.1取出队头元素放入t并弹出队头
        PII t=q.front();
        q.pop();
        //  2.2 for扩展入队
        for(int i=0;i<4;i++)
        {
            int x = t.x + dx[i], y = t.y + dy[i];
            //cout<<x<<" "<<y<<endl;
            if (x >= 0 && x < n && y >= 0 && y < m &&(g[x][y] == '.' ||g[x][y] == 'E')&& d[x][y] == -1)// 不出界
            {
                //cout<<"sucess"<<x<<" "<<y<<endl;
                //cout<<end.x<<end.y<<endl;
                d[x][y] = d[t.x][t.y] + 1;
                if (end == make_pair(x, y)) return d[x][y];
                q.push({x, y});    
            }
        }
    }
    return -1;
}
int main()
{
    int t;cin>>t;
    while(t--)
    {
        cin>>n>>m;
        for(int i=0;i<n;i++) scanf("%s",g[i]);
        PII start,end;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(g[i][j]=='S') start={i,j};
                else if(g[i][j]=='E') end={i,j};
            }
        }
        int distance_ =bfs(start,end);
        if(distance_==-1) puts("oop!");
        else printf("%d\n",distance_);
    }
    return 0;
}

2.844. 走迷宫 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
#define x first
#define y second
const int N=110;
int n,m;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int g[N][N], d[N][N];

int bfs()
{
    memset(d,-1,sizeof d);
    //1.创造队列,并入队
    queue<PII> q;
    q.push({0,0});
    d[0][0]=0;
    //2.非空队列
    while(q.size())
    {
        //2.1传入t,队列头删
        PII start=q.front();
        q.pop();
        //2.2拓展t
        for(int i=0;i<4;i++)
        {
            int x=start.x+dx[i],y=start.y+dy[i];
             if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1)
            {
                d[x][y]=d[start.x][start.y]+1;
                q.push({x,y});
            }
        }
    }
    return d[n-1][m-1];
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            cin>>g[i][j];
        }
    }
    cout<<bfs()<<endl;
    return 0;
}

Flood fill算法解析

核心概念:

洪水填充(Flood fill)算法:从一个起始节点开始把附近与其连通的节点提取出或填充成不同颜色颜色,直到封闭区域内的所有节点都被处理过为止,是从一个区域中提取若干个连通的点与其他相邻区域区分开(或分别染成不同颜色)的经典算法。

面试题 08.10. 颜色填充 - 力扣(LeetCode)

class Solution {
public:
    const int dx[4]={0,1,0,-1};
    const int dy[4]={1,0,-1,0};
    
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) 
    {
        int curcolor=image[sr][sc];
        if(curcolor==newColor)
        {
            return image;
        }
        int m=image.size(),n=image[0].size();
        //1.创建队列,入队
        queue<pair<int,int>> que;
        que.push({sr,sc});//push需要改成{}
        image[sr][sc]=newColor;
        //2.while非空
        while(que.size())
        {
            //2.1que取入t并删除
            auto t=que.front();
            que.pop();
            //2.2拓展t
            for(int i=0;i<4;i++)
            {
                int x=t.first+dx[i],y=t.second+dy[i];
                if(x<0||x>=m||y<0||y>=n) continue;
                if(image[x][y]!=curcolor) continue;
                que.emplace(x,y);//emplace可以直接传入
                image[x][y]=newColor;
            }
        }
        return image;
    }
};

class Solution {
public:
    const int dx[4]={0,1,0,-1};
    const int dy[4]={1,0,-1,0};
    void dfs(vector<vector<int>>& image, int sr, int sc,int curcolor, int newColor) 
    {
        if(image[sr][sc]==curcolor)
        {
            image[sr][sc]=newColor;
            for(int i=0;i<4;i++)
            {
                int x=sr+dx[i],y=sc+dy[i];
                if (x >= 0 && x < image.size() && y >= 0 && y < image[0].size()) 
                {
                    dfs(image, x, y, curcolor, newColor);
                }
            }
        }
    }
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) 
    {
        int curcolor=image[sr][sc];
        if(curcolor==newColor)
        {
            return image;
        }
        dfs(image,sr,sc,curcolor,newColor);
        return image;
    }
};

3.(flood fill算法)1113. 红与黑 - AcWing题库

1.bfs

//我想说一下这个题能用bfs的原理,我一开始对这个cnt会不会加重有疑惑
//然后最后我发现比如一开始往两个方向走,cnt都从1的状态变化
//而且那么比如第二部另一个走到了尽头那么cnt=2结束了,而另一个也是cnt=2,可是会仅需往前走,继续++
#include<bits/stdc++.h>
using namespace std;
const int N=30;
char g[N][N];
bool st[N][N];//判重数组
typedef pair<int,int> PII;
#define x first
#define y second
int m,n;
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int bfs(PII start)
{
    int cnt=1;
    st[start.x][start.y]=true;
    //1.创建队列并入队
    queue<PII> q;
    q.push(start);
    //2.while(队列不为空)
    while(q.size())
    {
        //  2.1入t删队列
        PII t=q.front();
        q.pop();
        //  2.2for(拓展t)
        for(int i=0;i<4;i++)
        {
            int x=t.x+dx[i],y=t.y+dy[i];
            if(g[x][y] !='.') continue;
            else if(st[x][y]) continue;
            else if(x<0||x>=m||y<0||y>=n) continue;
            st[x][y]=true;
            q.push({x,y});
            cnt++;
        }
    }
    return cnt;
}
int main()
{
    while(cin>>n>>m,n||m)
    {
        memset(st,0,sizeof(st));
        for(int i=0;i<m;i++) scanf("%s",g[i]);
        PII start;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                //cout<<g[i][j]<<" ";
                if(g[i][j]=='@') start=make_pair(i,j);
            }
            //cout<<endl;
        }
        cout<<bfs(start)<<endl;
    }
    return 0;
}

2.dfs

//同样dfs也是一样的,因为和bfs一样都使用了for(循环i)
//然后比如第一条线路全贯通cnt就得出答案了,其他的都是平行的另几个cnt;
//我想说一下这个题能用bfs的原理,我一开始对这个cnt会不会加重有疑惑
//然后最后我发现
#include<bits/stdc++.h>
using namespace std;
const int N=30;
char g[N][N];
typedef pair<int,int> PII;
#define x first
#define y second
int m,n;
int cnt;
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};

void dfs(int x,int y)
{
    g[x][y]='#';
    cnt++;
    //cout<<x<<" "<<y<<endl;
    for(int i=0;i<4;i++)
    {
        int a=x+dx[i],b=y+dy[i];
        // if(a<0 || a>=m || b<0 || b>=n || g[a][b]=='#') continue;
        // dfs(a,b);
        //cout<<a<<" "<<b<<" ";
        //cout<<g[x][y]<<endl;
        if(a>=0&&a<m&&b>=0&&b<n&&g[a][b]!='#')
        {
            //cout<<" sucess"<<endl;
            dfs(a,b);
        }
    }
}
int main()
{
    while(cin>>n>>m,n||m)
    {
        cnt=0;
        for(int i=0;i<m;i++) scanf("%s",g[i]);
        PII start;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                //cout<<g[i][j]<<" ";
                if(g[i][j]=='@') start=make_pair(i,j);
            }
            //cout<<endl;
        }
        dfs(start.x,start.y);
        cout<<cnt<<endl;

    }
    return 0;
}

4.(!!!注意语法flood fill算法)AcWing 1233. 全球变暖 - AcWing

//要找几个岛屿被淹没了
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
char g[N][N];
bool st[N][N];
typedef pair<int,int> PII;
#define x first
#define y second
const int dx[4]={0,1,0,-1};
const int dy[4]={1,0,-1,0};
int n;

void bfs(int i,int j,int* total,int* bound)
{
    //1.创建队列让其入队
    queue<PII> que;
    que.emplace(i,j);
    st[i][j]=true;
    //2.while(非空)
    while(que.size())
    {
        //2.1取出删除
        PII t=que.front();
        que.pop();
        (*total) ++;
        bool is_bound=false;
        for(int i=0;i<4;i++)
        {
            int x=t.x+dx[i],y=t.y+dy[i];
            if (x < 0 || x >= n || y < 0 || y >= n) continue;  // 出界
            if (st[x][y]) continue;
            if (g[x][y] == '.')//非常巧妙:这里是来判断当前节点的;
            {
                is_bound = true;
                continue;
            }
            que.emplace(x,y);
            st[x][y] = true;
        }
        if(is_bound) (*bound) ++;
    }
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) scanf("%s",g[i]);
    int cnt=0;
    //进行查找然后进行flood fill 的灌溉
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(!st[i][j]&&g[i][j]=='#')
            {
                int total=0,bound=0;
                bfs(i,j,&total,&bound);
                if(total==bound) cnt++;
            }

        }
    }
    cout<<cnt<<endl;
    return 0;
}

5.(三维走迷宫)1096. 地牢大师 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int l,r,c;
char g[N][N][N];
int dist[N][N][N];
struct Point
{
    int x, y, z;
};
int dx[6]={1,-1,0,0,0,0};
int dy[6]={0,0,1,-1,0,0};
int dz[6]={0,0,0,0,-1,1};
int bfs(Point start,Point end)
{
    memset(dist, -1, sizeof dist);
    //1.
    queue<Point> que;
    que.emplace(start);
    dist[start.x][start.y][start.z]=0;
    //2.
    while(que.size())
    {
        //2.1
        auto t=que.front();
        que.pop();
        //2.2
        for(int i=0;i<6;i++)
        {
            int x = t.x + dx[i], y = t.y + dy[i], z = t.z + dz[i];
            if (x < 0 || x >= l || y < 0 || y >= r || z < 0 || z >= c) continue;  // 出界
            if (g[x][y][z] == '#') continue;  // 有障碍物
            if (dist[x][y][z] != -1) continue;  // 之前走到过

            dist[x][y][z] = dist[t.x][t.y][t.z] + 1;
            if (x == end.x && y == end.y && z == end.z) return dist[x][y][z];
            que.push({x, y, z});
        }
    }
    return -1;
}
int main()
{
    while(cin>>l>>r>>c&&(l||r||c))
    {
        Point start,end;
        for(int i=0;i<l;i++)
            for(int j=0;j<r;j++)
                for(int p=0;p<c;p++)
                {
                    cin>>g[i][j][p];
                    if(g[i][j][p]=='S')  start={i,j,p};
                    if(g[i][j][p]=='E')  end.x=i,end.y=j,end.z=p;
                }
        int ans=bfs(start,end);
        if(ans==-1) cout<<"Trapped!"<<endl;
        else cout<<"Escaped in "<<ans<<" minute(s)."<<endl;
    }
    return 0;
}

十一,图论

1.1224. 交换瓶子 - AcWing题库

//暴力枚举
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int a[N];
int t;
int main()
{
    cin>>t;
    for(int i=1;i<=t;i++) cin>>a[i];
    int sum=0;
    //暴力肯定好想,但问题是优化:双指针
    //其中一个就是让右指针遍历到a[j]==i的时候,这样就是不断往右移动的这么一个顺序
    for(int i=1;i<=t;i++)
    {
        if(a[i]!=i)
        {
            for(int j=i+1;j<=t;j++)
            {
                if(a[j]==i)
                {
                    swap(a[j],a[i]);
                    sum++;
                }
            
            }            
        }
    }
    cout<<sum<<endl;
    return 0;
}

[外链图片转存中…(img-Q3lXAwZO-1711015082498)]

image

//图论
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int a[N],st[N];
int t;
int main()
{
    cin>>t;
    for(int i=1;i<=t;i++) cin>>a[i];
    int cnt=0;
    for(int i=1;i<=t;i++)
    {
        if(!st[i])
        {
            cnt++;
            for(int j=i;!st[j];j=a[j])
            {
                st[j]=true;
            }
        }
    }
    cout<<t-cnt<<endl;
    return 0;
}

2.1207. 大臣的旅费 - AcWing题库

[外链图片转存中…(img-wvt7fFXV-1711015082499)]

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct edge{
    int id,distance;  
};
vector<edge> h[N];
int n;
int dist[N];
void dfs(int u,int father,int x)
{
    dist[u]=x;
    for(auto node:h[u])
    {
        if(node.id!=father)
        {
            dfs(node.id,u,x+node.distance);
        }
    }
}
int main()
{
    cin>>n;
    for(int i=0;i<n-1;i++)
    {
        int a,b,c;cin>>a>>b>>c;
        h[a].push_back({b,c});//将边传入
        h[b].push_back({a,c});//将边传入
    }
    //随便选一个点为x
    //以x为单位计算所有的距离
    dfs(1,-1,0);
    //找出距离x最远的点y
    int u=1;
    for(int i=1;i<=n;i++)
    {
        if(dist[i]>dist[u])
        {
            u=i;
        }
    }
    
    //3.
    dfs(u,-1,0);
    //从y开始遍历,找到离y最远的点,与y最远的点的距离就是本题答案,即最远的点
    for(int i=1;i<=n;i++)
    {
        if(dist[i]>dist[u])
        {
            u=i;
        }
    }
    int s=dist[u];
    printf("%lld",10*(long long)s+(s*(s+1ll))/2);
    
    return 0;
}

十二,贪心(以小见大)

1.1055. 股票买卖 II - AcWing题库

image

#include<bits/stdc++.h>
using namespace std;
//贪心的核心就是从局部看全面
const int N=100010;
int a[N];
int main()
{
    int n;cin>>n;
    long long sum=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        int panduan=a[i]-a[i-1];
        if(i==1) continue;
        if(panduan>0) sum+=panduan;
    }
    cout<<sum<<endl;
    
    
    return 0;
}

2.104. 货仓选址 - AcWing题库

image

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int loc[N];
typedef long long LL;
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>loc[i];
    }
    sort(loc+1,loc+1+n);
    int mid=loc[(n+2)/2];
    LL res = 0;
    for (int i = 1; i <= n; i ++ ) res += abs(loc[i] - mid);
    //这里普及一下向上取整(a+b-1)/b 这里a=n+1 b=2
    printf("%lld\n", res);
    return 0;
}

3.122. 糖果传递 - AcWing题库

[外链图片转存中…(img-pRK74vj3-1711015082500)]

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000010;
int a[N];
long long c[N];
int main()
{
    int n;cin>>n;
    for (int i = 1; i <= n; i ++ )cin>>a[i];
    ll sum=0;for(int i=1;i<=n;i++) sum+=a[i];
    //
    ll avg=sum/n;
    for (int i = n; i > 1; i -- )
    {
        c[i] = c[i + 1] + avg - a[i];
    }
    c[1]=0;
    //在数轴上找一点x,使x道各个c[i]的距离之和最小,那么就像谷仓选址一样,最小为中位数
    sort(c+1,c+n+1);
    ll res=0;
    for(int i=1;i<=n;i++)
    {
        res+=abs(c[i]-c[(n+2)/2]);
    }
    cout<<res<<endl;
    return 0;
}

4.112. 雷达设备 - AcWing题库

[外链图片转存中…(img-7n1FpRjN-1711015082500)]

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
struct segment
{
    double l,r;
    bool operator<(const segment& t)const
    {
        return r<t.r;
    }
}seg[N];
int main()
{
    int n,d;cin>>n>>d;
    //读入数据找区间
    bool failure=false;
    for(int i=0;i<n;i++)
    {
        int x,y;cin>>x>>y;
        if(y>d) failure=true;//当有雷达不能扫到的时候,直接-1
        else
        {
            double len=sqrt(d*d-y*y);
            seg[i].l=x-len,seg[i].r=x+len;
        }
    }
    if(failure) puts("-1");
    else
    {
        sort(seg,seg+n);
        int cnt=0;
        double last=-1e20;
        for(int i=0;i<n;i++)
        {
            if(last<seg[i].l)
            {
                cnt++;
                last=seg[i].r;
            }
        }
        cout<<cnt<<endl;
    }
    return 0;
}

十三,简单数论

欧几里德辗转相除法

int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}

AcWing 1246. 等差数列 - AcWing

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N];
int gcd(int a,int b)
{
    return b ? gcd(b,a % b) : a;
}
int main()
{
    int n;cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    sort(a,a+n);
    //找d
    int d=0;
    for(int i=1;i<n;i++) d=gcd(d,a[i]-a[i-1]);
    //输出答案
    if (!d) printf("%d\n", n);
    else printf("%d\n", (a[n - 1] - a[0]) / d + 1);
    return 0;
}

线性欧拉筛选法

【算法篇-数论】线性筛法(欧拉筛法)筛 n 以内的质数_线筛法-CSDN博客

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include<cstdio>
using namespace std;
const int N = 2 ^ 20 + 10;
int primes[N], cnt;//pri[i]表示存放的质数,cnt记录质数个数,全局变量默认是0
bool st[N];//记录是不是合数,全局变量默认false
void get_primes(int n)
{
	for (int i = 2; i <= n; i++)从 2 开始筛
	{
		if (!st[i])//如果没有被划掉(表示不是合数)就说明当前i是质数
		{
			primes[cnt++] = i;//从primes[0]开始存放
		}
		for (int j = 0; primes[j] * i <= n; j++)//开始枚举已记录的质数,利用这些质数划掉合数
		{
			//for循环里面,i * primes[j] <= n,首先要保证划掉的合数不能大于n,不然没意义
			st[primes[j] * i] = true;//划掉合数
			if (i % primes[j] == 0) break;              
            //如果i是质数,则最多枚举到自身中断
			//如果i是合数,则最多枚举到自身的最小质数中断
		}
	}
}
int main()
{
	get_primes(12);
	for (int i = 0; i < cnt; i++)
	{
		cout << primes[i] << " ";
	}
	cout << endl;
	return 0;
}

1205. 买不到的数目 - AcWing题库(不会记结论)

#include<iostream>

using namespace std;

int main()
{
    int p,q;cin>>p>>q;
    cout<<(p - 1)*(q - 1)-1<<endl;
    return 0;
}

AcWing 1211. 蚂蚁感冒 - AcWing

#include<iostream>

using namespace std;

int x[55];

int main()
{
    int n;cin>>n;
    for(int i=0;i<n;i++) cin>>x[i];
    int left=0,right=0;//分别表示 左边向右 的 蚂蚁数量,   右边向左  的蚂蚁数量
    for(int i=1;i<n;i++)
    {
        if(  abs(x[i])<abs(x[0])  &&x[i]>0 ) left++;//统计头朝右的感染蚂蚁左边的数量
        else if(abs(x[i])>abs(x[0])  &&x[i]<0 ) right++;//统计头朝左的感染蚂蚁右边的数量
    }
    //如果初始蚂蚁向右,并且头朝左的数量为0的时候
    //如果初始蚂蚁向左,并且头朝右的数量为0的时候
    if (x[0] > 0 && right == 0 || x[0] < 0 && left == 0) cout << 1 << endl;
    
    else cout<<left+right+1<<endl;
    
    return 0;
}

1216. 饮料换购 - AcWing题库

#include<iostream>
using namespace std;

int main()
{
    int n;cin>>n;
    int res=n;
    while(n>=3)
    {
        res += n/3;
        n=n/3 +n%3;
    }
    cout<<res<<endl;
    return 0;
}
  • 35
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值