双向搜索

双向搜索(meet in the middle),用于处理数据量小但是还没达到暴搜要求,比如 \(O(2^{40})\) ,使用双向搜索可以优化到 \(O(2^{20})\)

算法如其名,从两端同时搜索最后在中间相遇,每端搜索只需要处理一半数据量。

题目1 很难评,写了一个假的双向搜索然后 \(tle\)\(t\) 疯了。。

简单分析对于 \(n\leq 20\) 一共需要 \(2*n-2\) 步走到终点,每一步都有两个选择,所以暴搜一共 \(2^{(2*n-2)}\) 中情况,显然是不可行的,但是 \(2^{(n-1)}\) 就ok。所以考虑双向搜索(注意走到斜对角线时 \(return\) ,因为没写导致 \(t\) 麻了,自以为没错误实则是假双搜)。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define inf 0x3f3f3f3f
#define db double
#define il inline
#define x first
#define y second
#define endl '\n'
const int N=3e5+5;
const int mod=998244353;
int a[30][30],n,ans=0;
unordered_map<int,int>mp[30];
int safe(int x,int y)
{
    if(x>=1&&x<=n&&y>=1&&y<=n) return 1;
    return 0;
}
void dfs1(int x,int y,int sum)
{
    sum^=a[x][y];
    if(x+y==n+1) 
    {
        mp[x][sum]++;
        return;
    }
    if(safe(x+1,y)) dfs1(x+1,y,sum);
    if(safe(x,y+1)) dfs1(x,y+1,sum);
}
void dfs2(int x,int y,int sum)
{ 
    if(x+y==n+1) 
    {
        ans+=mp[x][sum];
        return;
    }
    sum^=a[x][y];
    if(safe(x-1,y)) dfs2(x-1,y,sum);
    if(safe(x,y-1)) dfs2(x,y-1,sum); 
}
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=n;j++)
            cin>>a[i][j];
    dfs1(1,1,0);
    dfs2(n,n,0);
    cout<<ans<<endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int T=1;//cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}

题目2 首先纯暴力 \(O(2^{80})\) ,就给了一个很明确的方向,就是分成四份暴搜。首先对于每次向左/右转,一定改变方向是与 \(x/y\) 轴平行。因此将初始序列分成奇偶位置两个数组分别暴搜;其次对于分完的数组在进行双向搜索,前半部分暴搜的结果存入一个 \(map\) ,在对后半部分进行暴搜时,对于每一个结果 \(res\) ,查询 \(map\) 中是否存在 \(x/y-res\)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define inf 0x3f3f3f3f
#define db double
#define il inline
#define x first
#define y second
#define endl '\n'
const int N=2e5+5;
const int mod=998244353;
int a[100],ji[50],ou[50],q[25],h[25];
int n,x,y;
vector<int>v;
int op(int b[],int xy,int cd)
{
    v.clear();
    memset(q,0,sizeof q);
    memset(h,0,sizeof h);
    int s1=(n+3)/4,s2=cd-(n+3)/4;
    for(int i=1;i<=s1;i++) q[i]=b[i];
    for(int i=s1+1;b[i]!=0;i++) h[i-s1]=b[i];
    unordered_map<int,int>mp,vis;
    for(int s=0;s<(1<<(s1));s++)
    {  
        int sum=0;
        for(int i=0;i<(n+3)/4;i++)
        {
            if(s>>i&1) sum+=q[i+1];
            else sum-=q[i+1];
        }
        vis[sum]=1;
        mp[sum]=s;
    }
    for(int s=0;s<(1<<s2);s++)
    {
        int sum=0;
        for(int i=0;i<s2;i++)
        {
            if(s>>i&1) sum+=h[i+1];
            else sum-=h[i+1];
        }
        if(vis[xy-sum])
        {
            int t=mp[xy-sum];
            for(int i=0;i<s1;i++) v.push_back(t>>i&1);
            for(int i=0;i<s2;i++) v.push_back(s>>i&1);
            return 1;
        }
    }
    return 0;
}
void solve()
{
    cin>>n>>x>>y;
    //奇数对应y,偶数对应x
    for(int i=1;i<=n;i++) 
    {
        cin>>a[i];
        if(i&1) ji[(i+1)/2]=a[i];
        else ou[i/2]=a[i];
    }
    if(op(ji,y,(n+1)/2))
    {
        vector<int>j=v;
        if(op(ou,x,n/2))
        {
            vector<int>o=v;
            cout<<"Yes"<<endl;
            //cout<<j.size()<<" "<<o.size()<<endl;
            //ls表示当前正方向,1右,2上,3左,4下
            //j和o中1表示向上、右走
            int cnt=0,ls=1;
            for(int i=1;i<=n;i++)
            {
                if(i&1) 
                {
                    if(j[cnt]==1) //上
                    {
                        if(ls==1) cout<<'L'; 
                        else if(ls==3) cout<<'R';
                        ls=2;
                    }
                    else if(j[cnt]==0) //下 
                    {
                        if(ls==1) cout<<'R'; 
                        else if(ls==3) cout<<'L';
                        ls=4;
                    }
                }
                else 
                {
                    if(o[cnt]==1) //右
                    {
                        if(ls==2) cout<<'R'; 
                        else if(ls==4) cout<<'L';
                        ls=1;
                    }
                    else if(o[cnt]==0) //左
                    {
                        if(ls==2) cout<<'L'; 
                        else if(ls==4) cout<<'R';
                        ls=3;
                    }
                    cnt++;
                }
            }
            return;
        }
    }
    cout<<"No"<<endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int T=1;//cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}
posted @ 2024-07-08 19:01  mhw-mathcode  阅读(58)  评论(0)    收藏  举报