复杂算法分析(后续实时更新)

发布时间 2023-12-30 00:46:33作者: 月下~观星

复杂算法总结

1.dfs

模板样例

//走河卒(适用低数据复杂度)

#include<iostream>
using namespace std;
int n,m,ans,mx,my;
int vis[26][26];
int dx[]={0,1},dy[]={1,0},dX={1,-1},dY={2,-2};
int ju(int x,int y)
{
    return x>=0&&x<=n&&y>=0&&y<=n;
}
void dfs(int x,int y)
{
    if(x==n&&y==m)
    {
        ans++;
        return;
    }
    for(int i=0;i<2;i++)
    {
        int nx=dx[i]+x,ny=y+dy[i];
        if(ju(nx,ny)||vis[nx][ny])continue;
        dfs(nx,ny);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie();
    cout.tie();
    cin>>n>>m>>mx>>my;
    for(int i=0;i<2;i++)//标记不能走的
        for(int j=0;j<2;j++)
        {
             mx+=dX[i],my+=dY[i];
            if(ju(mx,my))vis[mx][my]=1;
            mx+=dX[j],my+=dY[i];
            if(ju(mx,my))vis[mx][my]=1;
        }
    dfs(0,0);
}

//迷宫

 #include <iostream>
using namespace std;
 int n, m, x1, y1, t, x, y, tx, ty, cnt;
 int a[6][6], book[6][6];
 int dx[4] = { 0, 0, 1, -1 };
 int dy[4] = { 1, -1, 0, 0 };
 void dfs(int x, int y) {
if (x == x1 && y == y1) {
    cnt++;
    return;
}

book[x][y] = 1;

for (int i = 0; i < 4; i++) {
    int nx = x + dx[i];
    int ny = y + dy[i];

    if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && book[nx][ny] == 0 && a[nx][ny] != -1) {
        dfs(nx, ny);
    }
}
book[x][y] = 0; // Reset the book[x][y] after backtracking
}

 int main() {
ios::sync_with_stdio(false);

cin >> n >> m >> t >> x >> y >> x1 >> y1;

for (int i = 1; i <= t; i++) {
    cin >> tx >> ty;
    a[tx][ty] = -1;
}
cnt = 0; // Initialize cnt
dfs(x, y);
cout << cnt << endl;
return 0;
 }

2.floodfill算法

链接https://www.acwing.com/activity/content/code/content/7518658/

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
char a[N][N];
int n,m;
bool v,vis[N][N];
int d[8][2]={{0,1},{1,0},{-1,0},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
void dfs(int x,int y)
{
    vis[x][y]=true;
    for(int i=0;i<8;i++)
    {
        int nx=d[i][0]+x,ny=y+d[i][1];
        if(a[nx][ny]=='W'&&nx>0&&nx<=n&&ny>0&&ny<=m&&!vis[nx][ny])
       dfs(nx,ny);
    }
}
int main()
{
    ios::sync_with_stdio(false);cin.tie();cout.tie();
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)cin>>a[i][j];
    long long cnt=0;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        if(a[i][j]=='W'&&!vis[i][j])
        {
            dfs(i,j);
             cnt++;
        }
    }
    cout<<cnt<<endl;
    return 0;
}


2.bfs

洛谷---地下城主

走迷宫模板

#include<iostream>
using namespace  std;
#include<queue>
char e[31][31][31];
int a, b, c;
int dx[] = { 0,0,1,-1,0,0 }, dy[] = { 1,-1,0,0,0,0 }, dz[] = { 0,0,0,0,1,-1 };
struct node {
    int x, y, z, t;//x代表横坐标,y代表纵坐标,z代表高坐标,t代表当前是第几个
};
queue<node>p;

bool ju(int x, int y, int z)
{
    return (x >= 1 && x <= b && y >= 1 && y <= c && z >= 1 && z <= a&& e[x][y][z] != '#') ? true : false;
}
int bfs(int x, int y, int z, int mx, int my, int mz)
{
    e[x][y][z] ='#';//代表当前点位已经走过
        p.push(node{ x,y,z });//入队
    while (!p.empty())//判断队列是否为空//循环判断枚举周围元素
    {
        int xx = p.front().x;
        int yy = p.front().y;
        int zz = p.front().z;
        int tt = p.front().t;
        p.pop();//出队
        for (int i = 0; i < 6; i++)
        {
          int  nx = xx + dx[i], ny = yy + dy[i], nz = zz + dz[i];
            if (nx == mx && ny == my && nz == mz)return tt + 1;
            if (ju(nx, ny, nz))
            {
                e[nx][ny][nz] = '#';//标志走过
                p.push(node{ nx,ny,nz,tt + 1 });//入队新的元素
            }
        }
    }
    return 0;
}
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> a >> b >> c;
    int i, j, k, i1, j1, k1;
    for (int z = 1; z <= a; z++)
        for (int x = 1; x <= b; x++)
            for (int y = 1; y <= c; y++)
            {
                cin >> e[x][y][z];
                if (e[x][y][z] == 'S')
                    i = x, j = y, k = z;
                else if (e[x][y][z] == 'E')
                    i1 = x, j1 =y, k1 = z;
            }
    int cnt = bfs(i, j, k, i1, j1, k1);
    if (cnt)
    {
        cout << "Escaped in " << cnt << " minute(s).";
    }
    else
        cout << "Trapped!";
    return 0;
}

3.hash表

hash用来解决映射,字符串比较问题

char a[100000]="anbsduijfbjafcnak";

如上是一个字符串

给定一个P作为标准值 通常为131与13331

将字符串看成一个p进制的数

ha[i]=ha[i-1]*P+a[i];//每个数值由ascill码值代替,递推式

p[i]=p[i-1]*P;//每一个阶段的P数用来实现字符串区间比较

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ull;
ull P=131;
ull a[10010];
char s[10010];
int n,ans=1;//ans初始化为1,因为至少一个序列
ull hashs(char s[])
{
    int len=strlen(s);
    ull ans=0;
    for (int i=0;i<len;i++)
        ans=ans*P+(ull)s[i];//计算hash值
    return ans;
}
main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s);
        a[i]=hashs(s);
    }
    sort(a+1,a+n+1);
    for (int i=2;i<=n;i++)//排序过了,所以相邻比较就行了
        if (a[i]!=a[i-1])
            ans++;
    printf("%d\n",ans);
}

4.双指针

1.双指针去重(set直接排序去重)

//o(nlogn)时间复杂度,主要是排序带来的
#include<iostream>
using namespace std;
#include<algorithm>
const int N = 10000;
int a[N];
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    sort(a + 1, a + 1 + n);
    int l = 1, r = 2;
    while (l <= r && l <=n)
    {
        if (a[l] != a[r])
        {
            cout << a[l] << ' ';
            l++, r++;
        }
        else
            l = r, r = r + 1;
               
    }
    return 0;
}

5.二分

//lower_bound upper_bound    

6.一维二维坐标转换

x*一行的元素数量+y

acwing--八数码问题
八数码问题(算法基础课)------经典bfs问题

//用map保存步数,如果考是否达到状态,用set去重也可以
#include<bits/stdc++.h>
using namespace std;
int dx[]={0,0,-1,1},dy[]={1,-1,0,0};
int bfs(string s)
{
    unordered_map<string,int>dist;
    string end="12345678x";
    queue<string>q;
    dist[s]=0;
    q.push(s);
    while(q.size())
    {
        auto t=q.front();q.pop();
        int r=dist[t];
          if(t==end)return r;
        int e=t.find('x');
        int x=e/3,y=e%3;
        for(int i=0;i<4;i++)
        {
            int nx=x+dx[i],ny=y+dy[i];
           int f=nx*3+ny;
           if(nx<0||nx>2||ny<0||ny>2)continue;
           swap(t[f],t[e]);
           if(dist[t]==0)
           {
               dist[t]=r+1;
           q.push(t);
           }
             swap(t[f],t[e]);           
        }
    }
    return -1;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie();cout.tie();
    string s;
 for(int i=1;i<=3;i++)
    for(int j=1;j<=3;j++)
    {
        char c;
        cin>>c;
        s+=c;
    }
    cout<<bfs(s);
    return 0;
}

跳蚱蜢(蓝桥杯)-----八数码类问题

用set,map去重都可以

7.并查集求反集

反集合并

在初始化并查集时我们初始化两倍于数据范围大小的并查集,超出数据范围部分称为反集,用来储存性质于并查集维护集合相反的集合。 并且两个集合中的元素性质互斥。 并查集的反集适用于只有元素只有两种性质的题目,也就是说,这个元素不属于并查集维护集合,则其必定属于另一个集合。

洛谷p1892----团伙

关于反集

我也是做这道题才知道的

如果a和b是敌人,合并n+b和a,n+a和b

如果c和a是敌人,合并n+c和a,n+a和c

那么b和c就并在一起了

这样就符合了题目敌人的敌人是朋友的规则

  for(int i=1;i<=n*2;i++)//初始化两倍数据范围
      p[i]=i;  
f[find(a+n)]=find(b);
 f[find(b+n)]=find(a);//反集合并
#include<bits/stdc++.h>
using namespace std;
int p[5010],q[5010];
int n,m;
int find(int x)
{
    if(x!=p[x])p[x]=find(p[x]);
    return p[x];
}

int main()
{
    ios::sync_with_stdio(false);cin.tie();cout.tie();
    cin>>n>>m;
      for(int i=1;i<=n*2;i++)
      p[i]=i;
    while(m--)
    {
        char a;
        cin>>a;
        if(a=='F')
        {
            int x,y;
            cin>>x>>y;
            p[find(x)]=find(y);
        }
        else
        {
            int x,y;
            cin>>x>>y;
            p[find(x+n)]=find(y);
            p[find(y+n)]=find(x);
        }
    }
    long long res=0;
   for(int i=1;i<=n;i++)if(p[i]==i)res++;
   cout<<res;
    return 0;
}