迭代加深,双向搜索,IDA*,A*,双端队列BFS

迭代加深:

//迭代加深搜索
//给搜索设定一个范围,如果在这个范围内没有答案那么再加大搜索范围
//这么做是为了防止搜索过深,导致利用大量时间搜索无用信息
//如果当前搜索是第10位,搜索的是个二叉树,那么前9个就是2^0+2^1+2^2+..+2^9=2^10-1,所以时间复杂度并没增大太多
 
//https://www.acwing.com/problem/content/172/

#include<bits/stdc++.h>
using namespace std;
const int N=500;
int path[N],n;
bool dfs(int now,int max_)
{
    if(now>max_) return false;
    if(path[now]==n) return true;
    bool vis[N]={0};
    for(int i=now;i>=1;i--) //剪枝,从大到小搜,可以更快到达目标值
        for(int j=i;j>=1;j--){
            int num=path[i]+path[j];
            if(vis[num]||num<=path[now]||num>n) continue;
            vis[num]=true,path[now+1]=num;
            if(dfs(now+1,max_)) return true;
        } 
    return false;
}
int main()
{
    path[1]=1; 
    while(cin>>n,n){
        int dep=1;
        while(!dfs(1,dep)) dep++;
        for(int i=1;i<=dep;i++) cout<<path[i]<<' ';
        cout<<endl;
    }
    return 0;
}

 

//https://www.luogu.com.cn/problem/P2346 
//迭代加深即可 
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int mp[N][N],mp_[N][N];
int res;
int dx[]={1,-1,0,0},dy[]={0,0,1,-1},opposite[4]={3,2,1,0};
bool check()
{
    for(int i=1;i<=4;i++){
        if(mp[i][1]==mp[i][2]&&mp[i][2]==mp[i][3]&&mp[i][3]==mp[i][4]) return true;
        if(mp[1][i]==mp[2][i]&&mp[2][i]==mp[3][i]&&mp[3][i]==mp[4][i]) return true;
    }
    if(mp[1][1]==mp[2][2]&&mp[2][2]==mp[3][3]&&mp[3][3]==mp[4][4]) return true;
    if(mp[1][4]==mp[2][3]&&mp[2][3]==mp[3][2]&&mp[3][2]==mp[4][1]) return true; 
    return false;
}
bool dfs(int depth,int max_depth,int now,int x1,int y1,int x2,int y2,int lst)
{
    if(depth==max_depth) return check();
    cout<<max_depth<<' '<<now<<endl;
    for(int i=0;i<4;i++){
        int x=x1+dx[i],y=y1+dy[i];
        if(x<1||x>4||y<1||y>4||opposite[i]==lst||mp[x][y]!=now) continue;
        mp[x1][y1]=mp[x][y],mp[x][y]=0;
        if(dfs(depth+1,max_depth,(now==1?2:1),x,y,x2,y2,i)) return true;
        mp[x][y]=mp[x1][y1],mp[x1][y1]=0;
    }
    for(int i=0;i<4;i++){
        int x=x2+dx[i],y=y2+dy[i];
        if(x<1||x>4||y<1||y>4||opposite[i]==lst||mp[x][y]!=now) continue;
        mp[x2][y2]=mp[x][y],mp[x][y]=0;
        if(dfs(depth+1,max_depth,(now==1?2:1),x1,y1,x,y,i)) return true;
        mp[x][y]=mp[x2][y2],mp[x1][y1]=0;
    }
    return false;
}
int main()
{
    int depth=0,x1=0,x2=0,y1=0,y2=0;
    for(int i=1;i<=4;i++)
        for(int j=1;j<=4;j++){
            char x; cin>>x;
            if(x=='B') mp[i][j]=1;
            else if(x=='W') mp[i][j]=2;
            else mp[i][j]=0;
            if(x=='O'&&x1==0) x1=i,y1=j;
            else if(x=='O') x2=i,y2=j; 
        }
    memcpy(mp_,mp,sizeof mp_);
     while(!dfs(0,depth,1,x1,y1,x2,y2,-1)) depth++;
     cout<<depth<<endl;
    res=depth,depth=0;
    memcpy(mp,mp_,sizeof mp);
    while(!dfs(0,depth,2,x1,y1,x2,y2,-1)) depth++;
    cout<<min(res,depth)<<endl;
    return 0;
}

 

 

 

双端队列-BFS优化搜索(适合0 1状态):

// https://www.luogu.com.cn/problem/P4667

//94 dijkstra
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
bool vis[N];
int n,m,dist[N];
typedef pair<int,int>pii;
int e[N],ne[N],h[N],idx,w[N];
void add(int a,int b,int c)
{
    w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool dijkstra()
{
    priority_queue<pii,vector<pii>,greater<pii> >que;
    que.push({0,1}),dist[1]=0;
    while(!que.empty()){
        pii now=que.top(); que.pop();
        int x=now.second,y=now.first;
        if(vis[x]) continue; vis[x]=true;
        for(int i=h[x];~i;i=ne[i]){
            int j=e[i];
            if(dist[j]>y+w[i]){
                dist[j]=y+w[i];
                que.push({dist[j],j});
            }
        }
    }
    return (dist[(n+1)*(m+1)]<0x3f3f3f3f/2);
}
int main() 
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=(n+1)*(m+1);i++) dist[i]=0x3f3f3f3f,h[i]=-1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            char x; cin>>x;
            if(x=='/'){
                int u=(m+1)*(i-1)+j+1,v=(m+1)*i+j;
                int u_=u-1,v_=v+1;
                add(u,v,0),add(v,u,0),add(u_,v_,1),add(v_,u_,1);
            }
            else{
                int u=(m+1)*(i-1)+j,v=(m+1)*i+j+1;
                int u_=u+1,v_=v-1;
                add(u,v,0),add(v,u,0),add(u_,v_,1),add(v_,u_,1);
            }
        }
    if(dijkstra()) cout<<dist[(n+1)*(m+1)];
    else cout<<"NO SOLUTION";
    return 0;
}


//双端队列优化
/*
很明显是一道搜索的题。

我们可以把电路板上的每个格点(横线与竖线的交叉点)看作无向图中的结点。
若两个结点x和 y是某个小方格的两个对角,则在 x与 y之间连边。
若该方格中的标准件(对角线)与x到y的线段重合,则边权为 0;若垂直相交,则边权为 1 (说明需要旋转 1 次才能连通)。
然后,我们在这个无向图中求出从左上角到右下角的最短距离,就得到了结果。

这是一个边权要么是 0 ,要么是 1的无向图。在这样的图上,我们可以通过双端队列广度搜索计算。
算法的整体框架与一般的广度优先搜索类似,只是在每个结点上沿分支扩展时稍作改变。
如果这条分支是边权为0的边,就把沿改分支到达的新节点从队头入队;
如果这条分支是边权为1的边,就像一般的广度优先搜索一样从队尾入队。
这样一来,我们就仍然能保证任意时刻广度优先搜索队列中的结点对应的距离值都具有"两段性"和“单调性”
每个结点从队头出队时,就能得到从左上角到该结点的最短距离。

因为每个结点只需要访问一次,所以算法的时间复杂度为O(n×m)。
*/
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=505;
char map[maxn][maxn];
bool used[maxn][maxn];
int dx[4]={1,1,-1,-1},
    dy[4]={1,-1,1,-1};
int n,m,dis[maxn][maxn];
deque< pair<int,int> >q,empty;
bool turn(int x1,int y1,int x2,int y2)
{
    int x=map[(x1+x2)/2][(y1+y2)/2]=='/'?1:-1;
    return x==(y1-y2)/(x1-x2);
}
void bfs()
{
    memset(used,0,sizeof(used));
    memset(dis,0x3f3f3f3f,sizeof(dis));
    q=empty;q.push_back(make_pair(0,0));dis[0][0]=0;//清零!!!
    while(!q.empty())
    {
        int a=q.front().first,
            b=q.front().second;
        q.pop_front();used[a][b]=true;
        for(int i=0;i<4;i++)
        {
            int x=dx[i]+a,y=dy[i]+b;
            if(x<0||n<x||y<0||m<y||used[x][y])
                continue;
            if(turn(a,b,x,y))//如果电线不直接连接两个点,即代价为1
            {//就把结点按正常入队
                dis[x][y]=min(dis[x][y],dis[a][b]+1);//注意要取最小值
                q.push_back(make_pair(x,y));
            }
            else//如果电线直连,那么到(a,b),(x,y)两个点的步数是一样的
            {//换句话说,他们的层级是一样的,由于一般的广搜的队列层级是单调递增的,所以为了保证这个性质,将(x,y)从队首入队.
                dis[x][y]=min(dis[x][y],dis[a][b]);
                q.push_front(make_pair(x,y));
            }
            if(x==n&&y==m) return;
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
            scanf("%s",map[i]);
        if((n+m)%2)
        {
            puts("NO SOLUTION");
            continue;
        }
        bfs();
        printf("%d\n",dis[n][m]);
    }
    return 0;
}

 

双向搜索:

//https://www.acwing.com/problem/content/description/173/

//双向DFS
//对于第一段,首先预处理出来前一半的所有数可以组成的数,然后再去搜索后半段的数组成的可能
//在搜索后半段的时候,对于每一个数都要和前半段的数拿出来对比一下,用二分的方法找到第一个加和 小于等于m的数然后更新最大值
 
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=50;
int n,m,k,w[N],weight[1<<25],res,cnt=1; //由于0也算状态之一,所以从1开始计数
bool cmp(int a,int b)
{
    return a>b;
}
void dfs1(int u,int sum)
{
    if(u>k){//前k个搜完,更新表 
        weight[cnt++]=sum;
        return;    
    }
    //选择搜还是不搜 
    dfs1(u+1,sum);
    if(w[u]+sum<=m) dfs1(u+1,sum+w[u]);
}
void dfs2(int u,int sum)
{
    if(u>n)//前n个都搜完了,开始判断 
    {
        int l=1,r=cnt;
        //二分出小于等于m值中最大的数 
        while(l<r){
            int mid=l+r+1>>1;
            if(weight[mid]+sum<=m) l=mid;
            else r=mid-1;
        }
        res=max(res,sum+weight[l]);
        return;
    }
    dfs2(u+1,sum);
    if(w[u]+sum<=m) dfs2(u+1,sum+w[u]);
}
signed main()
{
    cin>>m>>n;
    for(int i=1;i<=n;i++) cin>>w[i];
    //剪枝,从大到小排序,这样能够更快的搜 
    //优先搜索较大的数,优化搜索顺序
    //当前的和更大,当前的分支会更快的到达m,也就是使我们的搜索深度变浅 
    sort(w+1,w+1+n,cmp);
    k=n/2;
    dfs1(1,0);
    sort(weight+1,weight+1+cnt);
    //unique函数,用于去重操作,然后返回长度,若数组从1开始记得要-1; 
    cnt=unique(weig ht+1,weight+1+cnt)-weight-1;
    dfs2(k+1,0);
    cout<<res<<endl; 
    return 0;
}

 

启发式迭代加深 IDA* :

//启发式迭代加深 IDA*

//主要思想: 在迭代加深的搜索过程中,估价函数<=真实值,所以在搜的时候如果我们接下来判断一下在下面这些步中
//是否一定超过max_step,如果超过我们可以提前退出,相当于一个优化剪枝
//就是在搜索过程中添加一个启发式的估计

//https://www.acwing.com/problem/content/182/
#include<bits/stdc++.h>
using namespace std;
const int N=16;
int n,a[N],w[5+1][N],t;
int f(){
    int cnt=0;
    for(int i=1;i<n;i++)
        if(a[i]+1!=a[i+1]) cnt++;
    return (cnt+2)/3; 
}
bool dfs(int depth,int max_depth)
{
    int lst=f();
    if(depth+lst>max_depth) return false;
    if(lst==0) return true;
    //类似于区间dp,枚举长度然后进行两段之间的交换 
    for(int len=1;len<=n;len++){
        for(int l=1;l+len-1<=n;l++){
            int r=l+len-1;
            for(int k=r+1;k<=n;k++){
                memcpy(w[depth],a,sizeof a);
                int y=l;
                for(int x=r+1;x<=k;x++,y++) a[y]=w[depth][x];
                for(int x=l;x<=r;x++,y++) a[y]=w[depth][x];
                if(dfs(depth+1,max_depth)) return true;
                memcpy(a,w[depth],sizeof a);//恢复状态 
            }
        }
    }
    return false;
}
int main()
{
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        int depth=0;
        while(depth<5&&!dfs(0,depth)) depth++; //depth小于5要放在前面判断 
        if(depth>=5) cout<<"5 or more"<<endl;
        else cout<<depth<<endl;
     }
    return 0;
}

 

/*
            1    2
            3    4
        5 6 7 8 9 10 11
            12 13
        14 15 16 17 18 19 20
            21 22
            23 24
*/


//https://www.luogu.com.cn/problem/UVA1343
//主要在于代码实现 
#include<bits/stdc++.h>
using namespace std;
const int N=30;
int mp_[8][7]={//在进行图形变换时,打表是种常用的方法 
    {0,2,6,11,15,20,22},
    {1,3,8,12,17,21,23},
    {10,9,8,7,6,5,4},
    {19,18,17,16,15,14,13},
    {23,21,17,12,8,3,1},
    {22,20,15,11,6,2,0},
    {13,14,15,16,17,18,19},
    {4,5,6,7,8,9,10},
};
    //反操作的序号                    中间的6块序号 
int opposite[8]={5,4,7,6,1,0,3,2},center[8]={6,7,8,11,12,15,16,17};
int mp[N],path[100];
int f()
{
    int sum[4]={0},maxx=0;
    for(int i=0;i<8;i++) sum[mp[center[i]]]++,maxx=max(maxx,sum[mp[center[i]]]);
    return 8-maxx;
}
void operate(int x)//操作函数 
{
    int t=mp[mp_[x][0]];
    for(int i=0;i<6;i++) mp[mp_[x][i]]=mp[mp_[x][i+1]];
    mp[mp_[x][6]]=t;
}
bool dfs(int depth,int max_depth,int lst) //剪枝,如果想上操作,那么下次不要向下操作,否则会抵消 
{
    int cur=f();
    if(cur+depth>max_depth) return false;
    if(cur==0) return true;
    for(int i=0;i<8;i++)
        if(opposite[i]!=lst){
            operate(i);
            path[depth]=i;
            if(dfs(depth+1,max_depth,i)) return true; 
            operate(opposite[i]);
        }
    return false;
}
int main()
{
    while(cin>>mp[0],mp[0]){
        for(int i=1;i<24;i++) cin>>mp[i];
        int depth=0;
        while(!dfs(0,depth,-1)) depth++;
        if(!depth) cout<<"No moves needed"<<endl;
        else for(int i=0;i<depth;i++) printf("%c",path[i]+'A');
        cout<<endl<<mp[6]<<endl;
    }
}

 

//https://www.luogu.com.cn/problem/P2324

/*
    一道IDA*的题目,晚上打了大部分代码,留着启发搜索函数明天打
    不出所料地,启发函数刚开始没有思路,后来经过一步步的调试,发现了一个点
    搜两部分,黑骑士和白骑士的部分,然后统计数目,如果此时黑骑士部分有4个不属于黑,白骑士有3个不属于白
    那么考虑最好情况,3个黑的换3个白的,剩下的那个空一定和黑的交换,即每次的最小次数就是两者的最大值 
    
    做完之后看了下题解hhh,没有我这个启发搜索思路,还是太弱了,只跑了300ms  =,= 
*/
#include<bits/stdc++.h>
using namespace std;
const int N=6;
char mp[N][N];
int t,x,y;
int dx[]={1,1,-1,-1,2,2,-2,-2},dy[]={2,-2,2,-2,1,-1,1,-1};
int opposite[8]={3,2,1,0,7,6,5,4};

int f()
{
    int black=0,white=0,cnt=0;
    for(int i=1;i<=5;i++) if(mp[1][i]!='1') black++;
    for(int i=2;i<=5;i++) if(mp[2][i]!='1') black++;
    for(int i=4;i<=5;i++) if(mp[3][i]!='1') black++;
    if(mp[4][5]!='1') black++;
    if(mp[2][1]!='0') white++;
    for(int i=1;i<=2;i++) if(mp[3][i]!='0') white++;
    for(int i=1;i<=4;i++) if(mp[4][i]!='0') white++;
    for(int i=1;i<=5;i++) if(mp[5][i]!='0') white++;
    return max(black,white);
}
bool dfs(int depth,int max_depth,int lst,int x1,int y1)
{
    int cnt=f();
//    cout<<cnt<<' '<<depth<<' '<<max_depth<<endl;
    if(cnt+depth>max_depth) return false;
    if(cnt==0) return true;
    for(int i=0;i<8;i++){
        int xx=x1+dx[i],yy=y1+dy[i];
        if(xx<1||xx>5||yy<1||yy>5||opposite[i]==lst) continue;
        mp[x1][y1]=mp[xx][yy],mp[xx][yy]='*';
        if(dfs(depth+1,max_depth,i,xx,yy)) return true;
        mp[xx][yy]=mp[x1][y1],mp[x1][y1]='*';
    }
    return false;
}
int main()
{
    cin>>t;
    while(t--){
        for(int i=1;i<=5;i++)
            for(int j=1;j<=5;j++){
                cin>>mp[i][j];
                if(mp[i][j]=='*') x=i,y=j;
            }
        int depth=0;
        while(depth<=15&&!dfs(0,depth,-1,x,y)) depth++;
        cout<<(depth<=15?depth:-1)<<endl;
//        exit(0);
    }
    return 0;
}

 

posted @ 2023-11-02 20:03  o-Sakurajimamai-o  阅读(52)  评论(0)    收藏  举报
-- --