首页 > 编程语言 >搜索算法

搜索算法

时间:2023-05-24 13:13:39浏览次数:39  
标签:dist int 搜索算法 st que return now

//DPS(深度搜索)
//n-皇后问题
//方法一(与数字全排列相似)
#include<bits/stdc++.h>
using namespace std;
const int N = 80;
int n,res=0;
char Q[N][N];
bool cow[N],dg[N],rdg[N];//dg,rdg是对角线和反对角线,cow是列;
void dfs(int u)
{
    if(u==n)
    {
        res++;
        for(int i=0;i<n;i++) puts(Q[i]);
        cout<<endl;
        return;
    }
    for(int i=0;i<n;i++)
        if(!cow[i]&&!dg[u+i]&&!rdg[n-u+i])//u+i是,根据对角线规律,每个对角线的点都等于行数+上列数.n-u+i是防止i-u为负数;
        {
            Q[u][i]='Q';
            cow[i]=dg[u+i]=rdg[n-u+i]=true;
            dfs(u+1);
            Q[u][i]='.';
            cow[i]=dg[u+i]=rdg[n-u+i]=false;//恢复现场
        }
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            Q[i][j]='.';
    dfs(0);//从第0行开始;
    cout<<res;
}
//方法二 原始方法,通俗易懂,全局搜索
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int n;
char Q[N][N];
bool cow[N],dg[N],rdg[N],row[N];//cow 列,row 行;
void dfs(int x,int y,int q)
{
    if(y==n) y=0,x++;
    if(x==n)
        {
            if(q==n)
        {
            for(int i=0;i<n;i++) puts(Q[i]);
            cout<<endl;
        }
        return;
        }
//    不放皇后
    dfs(x,y+1,q);
   // 放皇后
        if(!cow[y]&&!row[x]&&!dg[x+y]&&!rdg[x-y+n])
        {
            Q[x][y]='Q';
            cow[y]=row[x]=dg[x+y]=rdg[x-y+n]=true;
            dfs(x,y+1,q+1);
            cow[y]=row[x]=dg[x+y]=rdg[x-y+n]=false;
            Q[x][y]='.';
        }
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
                Q[i][j]='.';
    dfs(0,0,0);//行,列,已经放置的皇后数目;
    return 0;
}
//BFS(最短路径)
//例题链接:https://vjudge.net/problem/POJ-3984;
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
int g[N][N];
int d[N][N];
typedef pair<int,int> PII;
PII  q[N*N],p[N][N];
int bfs()
{
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
    q[0]={0,0};
    memset(d,-1,sizeof d);
    d[0][0]=0;
    int tt=0,hh=0;
    while(hh<=tt)
    {
        auto t=q[hh++];
        for(int i=0;i<4;i++)
        {
            int x=t.first+dx[i],y=t.second+dy[i];
            if(x<n&&x>=0&&y<m&&y>=0&&d[x][y]==-1&&g[x][y]==0)
            {
                d[x][y]=d[t.first][t.second]+1;
                p[x][y]=t;
                q[++tt]={x,y};
            }
        }
    }
    int x=n-1,y=m-1;
    while(x||y)
    {
        cout<<x<<","<<y<<endl;
        auto t=p[x][y];
        x=t.first;
        y=t.second;
    }
    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;
}
//方法二,stl((队列方法),基本思想,步骤与方法一相同.记录下所有空格的最短路径,然后输出所需空格的最短路径.
#include<bits/stdc++.h>
using namespace std;
int mp[5][5],vis[5][5];//map记录地图,vis记录这一点是否被访问过
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};//向右,向左,向上,向下移动
struct node{
    int x,y;//记录点的坐标
};
node pre[10][10];//记录某个节点的父节点
void BFS()
{
    queue<node> que;//STL   queue的应用
    node str;
    str.x=str.y=0;
    que.push(str);//(0,0)入队
    vis[0][0]=1;//标记(0,0)已被访问
    while(!que.empty())//队列不空时
    {
        node now=que.front();//用一个结点记录队首结点
        que.pop();//出队
        if(now.x==4&&now.y==4)//当前 访问的结点为(4,4),即全部访问完
            return;
        for(int i=0;i<4;i++)
        {
            node next;
            next.x=now.x+dir[i][0];//x往左或者往右移动
            next.y=now.y+dir[i][1];//y往上或者往下移动
            if(next.x>=0&&next.x<5&&next.y>=0&&next.y<5&&mp[next.x][next.y]==0&&vis[next.x][next.y]==0)
            //点next(x,y)在地图里,且地图里所对应的点是路而不是墙壁,且该点未被访问
            {
                vis[next.x][next.y]=1;//标记为已访问
                que.push(next);//下一个结点入队
                pre[next.x][next.y]=now;//记录一下next(next.x,next.y)这个结点的父节点是now
            }
        }
    }
}
void print(node cur)//倒序输出
{
    if(cur.x==0&&cur.y==0)
    {
        printf("(0, 0)\n");
        return;
    }
    print(pre[cur.x][cur.y]);   //递归输出这个结点父节点 ,最终的结果先输出(0,0)
    printf("(%d, %d)\n",cur.x,cur.y);//打印这个结点的坐标
}
int main()
{
    for(int i=0;i<5;i++)
        for(int j=0;j<5;j++)
            scanf("%d",&mp[i][j]);
    BFS();
    node ed;
    ed.x=ed.y=4;//定义结点ed是图的最右下角那个结点
    print(ed);
    return 0;
}
///朴素dijkstra算法 —— 模板题 AcWing 849. Dijkstra求最短路 I
///时间复杂是 O(n2+m)O(n2+m), nn 表示点数,mm 表示边数
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int n,m;
int g[N][N];  // 存储每条边
int dist[N];  // 存储1号点到每个点的最短距离
bool st[N];   // 存储每个点的最短路是否已经确定

// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);//将每个点每个距离赋值为无穷大
    dist[1] = 0;

    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;     // 在还未确定最短路的点中,寻找距离最小的点
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        // 用t更新其他点的距离
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        st[t] = true;
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    memset(g,0x3f,sizeof g);
    cin>>n>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=min(g[a][b],c);
    }
    int t=dijkstra();
    cout<<t<<endl;
}
///堆优化版dijkstra —— 模板题 AcWing 850. Dijkstra求最短路 II
///时间复杂度 O(mlogn)O(mlogn), nn 表示点数,mm 表示边数
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int h[N],e[N],ne[N],idx,n,m,w[N];
int dist[N];
bool st[N];
typedef pair<int ,int>PII;
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;/// 邻接表存储所有边
}
int dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    priority_queue<PII,vector<PII>,greater<PII>> heap;//创立小根堆
    heap.push({0,1});/// first存储距离,second存储节点编号
    while(!heap.empty())
    {
        auto t=heap.top();
        heap.pop();
        int ver=t.second,dis=t.first;
        if(st[ver]) continue;
        for(int i=h[ver];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dis+w[i])
            {
                dist[j]=dis+w[i];
                heap.push({dist[j],j});
            }
        }
    }
        if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    int t=dijkstra();
    cout<<t<<endl;
}
///最短路计数,使用邻接表动态数组,bfs
#include<bits/stdc++.h>
using namespace std;
const int N=1000010,mod=100003;
vector<int> g[N];
int dist[N];
bool vis[N];
int cnt[N];
int n,m;
void bfs()
{
    memset(dist,0x3f3f3f,sizeof dist);
    memset(vis,false,sizeof vis);
    queue<int>que;
    dist[1]=0;
    cnt[1]=1;
    que.push(1);
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        if(vis[u]) continue;
        vis[u]=true;
        for(auto v:g[u])
        {
            if(!vis[v])
            {
                if(dist[v]>dist[u]+1)///如果此时不是1最短路,更新最短路
                {
                    dist[v]=dist[u]+1;
                    cnt[v]=cnt[u];
                    cnt[v]%=mod;
                    que.push(v);
                }
                else if(dist[v]==dist[u]+1)///如果走的路是最短路,就在这个地方加上1;
                {
                    cnt[v]+=cnt[u];
                    cnt[v]%=mod;
                }
            }
        }
    }
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a,b;
        cin>>a>>b;
        g[a].push_back(b);///储存邻接表
        g[b].push_back(a);
    }
    bfs();
    for(int i=1;i<=n;i++)
        cout<<cnt[i]<<endl;
    return 0;
}
///ballman-ford
///存在负边时使用
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m,k;
int dist[N],backup[N];
struct node
{
    int a,b,w;
}edge[N];
int ballman_ford()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<k;i++)
    {
        memcpy(backup,dist,sizeof dist);
        for(int j=0;j<m;j++)
        {
            int a=edge[j].a,b=edge[j].b,w=edge[j].w;
            dist[b]=min(dist[b],backup[a]+w);
        }
    }
    if(dist[n]>0x3f3f/2) return -1;
    else return dist[n];
}
int main()
{
    cin>>n>>m>>k;
    for(int i=0;i<m;i++)
    {
        int a,b,w;
        cin>>a>>b>>w;
        edge[i]={a,b,w};
    }
    int t =ballman_ford();
    if(t==-1)  puts("impossible");
    else cout<<t<<endl;
    return 0;
}
///SPFA路径
///使用的邻接表储存
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m;
int h[N],e[N],ne[N],w[N],idx;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    queue<int>que;
    que.push(1);
    st[1]=true;
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        st[now]=false;
        for(int i=h[now];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[now]+w[i])
            {
                dist[j]=dist[now]+w[i];
                if(!st[j])
                {
                    que.push(j);
                    st[j]=true;
                }
            }
        }
    }
    if(dist[n]>0x3f3f/2) return -1;
    else return dist[n];
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    int t=spfa();
    if(t==-1) cout<<"impossible"<<endl;
    else cout<<t<<endl;
    return 0;
}
///二维数组存SPFA
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
struct node
{
    int b,w;
}e;
int n,m;
int dist[N];
bool st[N];
vector<node>g[N];
int spfa()
{
    memset(dist,0x3f3f,sizeof dist);
    dist[1]=0;
    queue<int>que;
    que.push(1);
    st[1]=true;///表示我们的这个点已经入队列了
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        st[now]=false;///队头出去了,那就是这个点不在这个队列里了标记false;
       for(int i=0;i<g[now].size();i++)
       {
           int v=g[now][i].b;
           if(dist[v]>dist[now]+g[now][i].w)
           {
               dist[v]=dist[now]+g[now][i].w;
               if(!st[v])///如果队列里面没有他,再加入队列;
               {
                   st[v]=true;
                   que.push(v);
               }
           }
       }
    }
    if(dist[n]>0x3f3f/2) return -1;
    else return dist[n];
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a;
        cin>>a>>e.b>>e.w;
        g[a].push_back(e);///存入数据
    }
    int t=spfa();
    if(t==-1) cout<<"impossible"<<endl;
    else cout<<t<<endl;
    return 0;
}
///SPFA判断负环
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
struct node
{
    int b,w;
}e;
int n,m;
int dist[N],cnt[N];
bool st[N];
vector<node>g[N];
bool spfa()
{
    queue<int>que;
    for(int i=1;i<=n;i++)///有可能从起点1,走不到负环的位置,所以1只需要把所有点都加入队列遍历即可
    {
        st[i]=true;
        que.push(i);
    }
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        st[now]=false;///队头出去了,那就是这个点不在这个队列里了标记false;
       for(int i=0;i<g[now].size();i++)
       {
           int v=g[now][i].b;
           if(dist[v]>dist[now]+g[now][i].w)
           {
               dist[v]=dist[now]+g[now][i].w;
               cnt[v]=cnt[now]+1;///这个代表的是当前这条路的边数
               if(cnt[v]>=n) return true;///如果,我是说如果当前路的边数已经大于n了,那很明显存在负环;
               if(!st[v])///如果队列里面没有他,再加入队列;
               {
                   st[v]=true;
                   que.push(v);
               }
           }
       }
    }
    return false;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a;
        cin>>a>>e.b>>e.w;
        g[a].push_back(e);///存入数据
    }
    if(spfa()) cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
    return 0;
}
///floyd算法,适用于多个询问,复杂度0n3
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m,k;
int d[N][N];
void floyd()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int x=1;x<=n;x++)
            d[j][x]=min(d[j][x],d[j][i]+d[i][x]);
}
int main()
{
    cin>>n>>m>>k;
    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]=1e9;
    while(m--)
    {
        int a,b,w;
        cin>>a>>b>>w;
        d[a][b]=min(d[a][b],w);
    }
    floyd();
    while(k--)
    {
        int a,b;
        cin>>a>>b;
        if(d[a][b]>1e9/2) cout<<"impossible"<<endl;
        else cout<<d[a][b]<<endl;
    }
    return 0;
}
///A-star搜索
///将bfs中的队列换成优先队列
///dist数组里除了存真实距离之外还需要存入估价距离
///3个小时理解+学新东西,要清楚这里面的估价距离是怎么算出来的,要知道哈希图函数的应用.
///此题为八数码难题,下面附上代码,下一个还有传统bfs,也是用到了哈希图;
#include<bits/stdc++.h>
//#define first x;
//#define second y;
using namespace std;
typedef pair<int,string>pis;
int f(string state)
{
    int res=0;
    for(int i=0;i<state.size();i++)
        if(state[i]!='x')
    {
        int t=state[i]-'1';
        res+=abs(i/3-t/3)+abs(i%3-t%3);
    }
    return res;
}
string bfs(string start)
{
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
    char op[]="urdl";
    string end="12345678x";
    unordered_map<string,int >dist;
    unordered_map<string,pair<char,string>> prev;
    priority_queue<pis,vector<pis>,greater<pis>>heap;
    dist[start]=0;
    heap.push({f(start),start});
    while(!heap.empty())
    {
        auto t=heap.top();
        heap.pop();
        string state=t.second;
        if(state==end) break;
        int x,y;
        for(int i=0;i<9;i++)
            if(state[i]=='x')
        {
            x=i/3,y=i%3;
            break;
        }
        string source=state;
        for(int i=0;i<4;i++)
        {
            int a=x+dx[i],b=y+dy[i];
            if(a<0||a>=3||b<0||b>=3) continue;
            state=source;
            swap(state[x*3+y],state[a*3+b]);
            if(!dist.count(state)||dist[state]>dist[source]+1)
            {
                dist[state]=dist[source]+1;
                prev[state]={op[i],source};
                heap.push({dist[state]+f(state),state});
            }
        }
    }
    string res;
    while(end!=start)
    {
        res+=prev[end].first;
        end=prev[end].second;
    }
    reverse(res.begin(),res.end());
    return res;
}
int main()
{
    string start,seq;
    char c;
    for(int i=0;i<9;i++)
    {
        cin>>c;
        start+=c;
        if(c!='x') seq+=c;
    }
    int num=0;
    for(int i=0;i<8;i++)
        for(int j=i;j<8;j++)
        if(seq[i]>seq[j]) num++;
    if(num%2) cout<<"unsolvable";
    else cout<<bfs(start)<<endl;
    return 0;
}
///传统bfs
///相较于A-star算法较为简便,但哈希图的搜索导致搜索时间大大减小
#include<bits/stdc++.h>
using namespace std;
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
char op[]="durl";
string ed="12345678x";
unordered_map<string,int>dist;
unordered_map<string,string>pre;
bool bfs(string s)
{
    queue<string>que;
    que.push(s);
    dist[s]=0;
    while(!que.empty())
    {
        string t=que.front();
        que.pop();
        string now=t;///由于后面t中x的位置要变换,所有要开一个copy,用于记录t变换后,t之前的模样,一对一的hash;
        if(t==ed) return true;
        int k=t.find('x');///找到x的位置,进行操作
        int x=k/3,y=k%3;///将一维的位置转换到二维
        for(int i=0;i<4;i++)
        {
            int a=x+dx[i],b=y+dy[i];
            if(a<0||a>=3||b<0||b>=3) continue;///不满足直接出局
            swap(t[k],t[a*3+b]);///进行转换
            if(!dist.count(t))///如果此时这个状态没被记录过,那就记录,如果被记录过,那就证明这条路肯定不是最短的次数,就直接跳过
            {
                dist[t]=i;///一对一hash,记录操作
                pre[t]=now;///一对一hash,记录变换前的string模样
                que.push(t);
            }
            swap(t[k],t[a*3+b]);
        }
    }
    return false;
}
int main()
{
    string st,res;
    for(int i=0;i<9;i++)
    {
        char c;
        cin>>c;
        st+=c;
    }
    if(bfs(st))///类似于小木棍问题,将搜索问题转化为bool函数
    {
        string t=ed;
        while(t!=st)
        {
            res=res+op[dist[t]];///记住,这里string类的+=与+算法不一样,所以在字符串拼接的时候最好不用+=,+=返回的是char类型,char类型不能想加
            ///详见:https://blog.csdn.net/weixin_43222324/article/details/106814953?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168378661516800222855326%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=168378661516800222855326&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-106814953-null-null.142^v86^control,239^v2^insert_chatgpt&utm_term=%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E7%9A%84%2B%3D%E4%B8%8E%2B&spm=1018.2226.3001.4187
            t=pre[t];
        }
        reverse(res.begin(),res.end());///由于是从尾到头,所以要逆序一下
        cout<<res;
    }
    else
        cout<<"unsolvable";
    return 0;
}
///染色法搜索
//https://www.luogu.com.cn/record/110255899
///这种包围类型的染色类型,可以通过在本题圈外面加一圈可以被搜的点,这样就可以搜到所有可以被搜的点了
///拿此题来说明,在外面加一层0,即使边是*也能也能搜到0;详细思路在https://www.luogu.com.cn/problem/P1162
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m,res;
char mp[N][N];
bool vis[N][N];
void dfs(int x,int y)///进行染色
{
    if(x<0||x>n+1||y<0||y>m+1||vis[x][y]) return;///如果这个点被染过或者这个点本来就是墙,就跳过
    vis[x][y]=true;
    for(int i=0;i<4;i++)
        dfs(x+dx[i],y+dy[i]);
}
void dfs1(int x,int y)
{
    for(int i=0;i<4;i++)
    {
        int x1=x+dx[i],y1=y+dy[i];
        if(x1>=1&&x1<=n&&y1>=1&&y1<=m&&!vis[x1][y1]&&mp[x1][y1]=='0')
        {
            res++;///每标记一个,就说明有一个存在;
            vis[x1][y1]=true;
            dfs(x1,y1);
        }
    }
    return;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
    {
        cin>>mp[i][j];
        if(mp[i][j]=='*') vis[i][j]=true;
    }
    dfs(0,0);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(!vis[i][j])
            {
                res++;
                vis[i][j]=true;
                dfs1(i,j);
            }
    cout<<res;
    return 0;
}
//https://www.luogu.com.cn/problem/P1077
///记忆化搜索,已经搜过的元素标记一下不需要再搜了,可以大大提高速度;
/**
    所有的记忆化搜索都可以转化为动态规划,但所有的动态规划不一定能转换成记忆化搜索 --clg;
**/
#include<bits/stdc++.h>
using namespace std;
const int N=1010,mod=1e6+7;
int n,m,a[N],f[N][N];
int dfs(int st,int num)///返回值函数,定义为int
{
    if(num>m) return 0;
    if(st==n+2) return 0;
    if(f[st][num]) return f[st][num];///如果这种方法已经搜过,直接返回这种方法的res就可以;
    if(num==m) return 1;///如果达到,说明这种方法可以,所以+1;
    int res=0;
    for(int i=0;i<=a[st];i++) res=(res+dfs(st+1,i+num))%mod;
    f[st][num]=res;///说明我这种情况已经全搜完啦,不用再搜啦,再看到就直接给你答案了;
    return res;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    cout<<dfs(1,0);
    return 0;
}

 

标签:dist,int,搜索算法,st,que,return,now
From: https://www.cnblogs.com/o-Sakurajimamai-o/p/17427989.html

相关文章