【刷题】【搜索】

1>生日蛋糕

做一个蛋糕,规定体积为n(pai),层数为n,

m,n,ri,hi均为整数

且每一层ri大于上一次rj,每一层高度hi大于hj

求最大的s表

先列方程:

V=n=∑ri*ri*hi 

S=rm*rm +∑2*ri*hi

(1<=ri<=m)

可以看出,这里有明显的枚举,写dfs的特征,

枚举层数,记录上次的r,h,和直到上一层的s,v,

直到最后一层,与V比较,等则更新ans

但是n<=20000,

最大的r是132,最大的h是20000,

过大,枚举过程肯定要剪枝,

剪枝分为

1)可行性剪枝(减v)

2)最优性剪枝(减s)

a.加上估值函数mn_v,mn_s,

分别表示前i层,半径1-i枚举,高度1-i枚举的值,

这样就为搜索过程提供了一个:可行下界

以后的s和v不会过大,

(在dfs前递推预处理)

b.感觉这个剪枝不太给力,

那么我们就再加入一点,减去更多的枝

看看这个v,

似乎还可以设置一个可行上界?!

就是每次的ri*ri*hi*res +v <V

不过这个因为和ri和hi的具体值有关(ri,hi不确定),所以放在枚举中较好

那么再看看mn_s,似乎存在着一个问题:

mn_s和mn_v不太贴合,这个mn_s太理想

如果我们去贴合mn_v这个量,就要加大一层的r或h,那s与mn_s就差的多了,

最后就算与ans比较,成功更新的概率不高,

所以我们想啊想,想着再换一种思路求s的界限

看看式子:s是从侧面来的,

将s与V建立联系,就可以让s与ans更接近,不会出现中间突然的偏差,

发现:

vi=ri*ri*hi = ri*hi*2 (/2)(*ri) =si

则:resv=(n-v)

S<= s+resv/rm*2 

所以完成:

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m;
int inf=99999999;
int mn_s[20],mn_v[20];
int ans;

int rd;
void dfs(int pos,int s,int v,int mr,int mh) 
{
    if(pos==0)
    {
        if(v==n) ans=min(ans,s);
        return ;
    }
    
    if(!mh || !mr) return ;
    if(s+ mn_s[pos] >ans) return ; 
    if(v+ mn_v[pos] >n) return ;
    if(s+ (n-v)/mr *2 >ans) return ;
    
    for(int i=mr;i;i--)
    {    
        if(pos==m) s=i*i;
        
        int mx_h=min(mh, (n-v-mn_v[pos-1])/(i*i));
        //printf("%d %d %d %d
",pos,s,v,mx_h);
        for(int j=mx_h;j>=pos;j--)
            dfs(pos-1,s+2*i*j,v+i*i*j,i-1,j-1);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    
    for(int i=1;i<=m;i++)//枚举的是层数,表示每层的最小s,v 
        mn_v[i]=mn_v[i-1] +i*i*i,
        mn_s[i]=mn_s[i-1] +i*i*2;//这里的mn_s不包括底面 
    
    ans=inf;
    dfs(m,0,0,sqrt(n),n);
    
    if(ans==inf) printf("0
");
    else printf("%d
",ans);
    
    return 0;
}

2>addition chain

一道不容易想,怎么剪枝的题

然后居然是迭代加深...

震惊,然后因为层数不多,我用的是优先队列去重,减少无用递归

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
using namespace std;
int n,maxd;
int d[10003],t[10003];

bool dfs(int pos)
{
    if(pos>maxd) return false;
    
    int nw;
    priority_queue <int> q;
    for(int i=pos-1;i;i--)
        for(int j=i;j;j--)
        {
            nw=d[i]+d[j] ;
            if(pos<maxd ) 
            {
                if((nw<<(maxd-pos))<n) break;
                //这里其实还可以多加一个可行性剪枝:计算之后可以到达的最大结果 
                if(nw<n && nw>d[pos-1]) q.push(nw);//注意这里还要求单调递增 
            }
            else if(nw==n)
            {
                for(int i=1;i<maxd;i++) 
                    printf("%d ",d[i]);
                printf("%d
",n);
                return true;
            }
        }
    
    bool ok=false;
    while(!q.empty() && !ok)
    {
        nw=q.top(); q.pop();
        while(!q.empty() && q.top()==nw) q.pop();
        d[pos]=nw;
        ok=dfs(pos+1);
    }
    return ok;
}

int main()
{
    d[1]=1,d[2]=2;
    while(~scanf("%d",&n) && n )
        if(n<4) 
        {
            for(int i=1;i<=n;i++) printf("%d ",i);
            printf("
");
        }
        else
        { 
            for(maxd=3; ;maxd++)if(dfs(3)) break;
        }
    
    return 0;
}

3>打开灯泡

 好坑啊

vector在元素很少的时候,不要用,会超时

用优先队列,或者双端队列,都行

#include<cstdio>
#include<cstdlib>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
int n,m;char c;
const int N=300003;
int sz0[N],sz1[N];
int g0[N][5],g1[N][5];

int step[N];
bool vis[N];
deque <int> q;

int main()
{
    cin>>n>>m;
    
    if((n+m)%2)
    {
        cout<<"NO SOLUTION";
        return 0;
    }
    
    int pa=1,pb=2,pc=pa+m+1,pd=pc+1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>c;
            if( c=='/' )
            { 
                g0[pb][sz0[pb]++]=pc,g0[pc][sz0[pc]++]=pb;
                g1[pa][sz1[pa]++]=pd,g1[pd][sz1[pd]++]=pa;  
            }
            else
            {
                g1[pb][sz1[pb]++]=pc,g1[pc][sz1[pc]++]=pb;
                g0[pa][sz0[pa]++]=pd,g0[pd][sz0[pd]++]=pa;  
            }
            pa++,pb++,pc++,pd++;
        }
        pa++,pb++;
        pc++,pd++;
    }
    
    int ed=(n+1)*(m+1);
    q.push_front(1); 
    step[1]=1;
    while(!q.empty() )
    {
        int t=q.front()  ;q.pop_front() ;
        if(vis[t]) continue;
        vis[t]=true;
        if(t ==ed)
        {
            cout<<step[t]-1;
            return 0;
        }
        
        for(int i=0;i<sz0[t ];i++)
        {
            int v=g0[t ][i];
            if(step[v] && step[v]<=step[t] ) continue;
            step[v]=step[t] ,q.push_front(v) ; 
            if(v ==ed)
            {
                cout<<step[t]-1;
                return 0;
            }
        }
        for(int i=0;i<sz1[t ];i++)
        {
            int v=g1[t ][i];
            if(step[v] && step[v]<=step[t] +1) continue;
            step[v]=step[t] +1,q.push_back(v) ; 
        }
    }
    return 0;
}

4>埃及分数

很烦的分数操作,用上了高精度中学的结构体操作,不错,但是爆了int

以后注意吧

#include<cstdio>
#include<cstdlib>
#define int long long
using namespace std;
int gcd(int a,int b)
{
    if(!b) return a;
    else return gcd(b,a%b); 
};
struct fs
{
    int a,b;// a/b 
    void yuefen()
    {
        if(a>1)
        {
            int g=gcd(a,b);
            a/=g,b/=g;
        }
    }
}; 
fs operator - (fs &a,fs &b)
{
    fs c;
    c.a =a.a *b.b -b.a *a.b ;
    c.b =a.b *b.b ;
    c.yuefen() ;
    return c;
}
bool cmp (fs &a,fs &b)
{
    fs c=a,d=b;
    c.b *=b.b ,c.a *=b.b ;
    d.b *=a.b ,d.a *=a.b ;
    return c.a <d.a ;
}
fs operator * (fs &a,int &b)
{
    fs c=a;
    c.a *=b;
    c.yuefen() ;
    return c;
}

bool ok;
int A,B,mx;
int d[100000],ans[100000];
void dfs(fs nw,int dep,fs ll)
{
    if(dep==mx) 
    {
        if(nw.a ==1 && (nw.b <ans[mx] || !ans[mx] ) )
        {
            ok=true;
            for(int i=1;i<mx;i++) 
                ans[i]=d[i];
            ans[mx]=nw.b ;
        }
        return ;
    }
    
    ll.b ++;
    int cnt=mx-dep+1;
    fs t=ll*cnt;
    while(cmp(nw,ll) || nw.b ==ll.b ) ll.b ++;
    while(cmp(nw ,t) ) 
    {
        d[dep]=ll.b ;
        fs check=nw-ll;
        dfs(check,dep+1,ll); 
        ll.b ++;
        t=ll*cnt;
    }
}

signed main()
{
    scanf("%lld%lld",&A,&B);
    fs t,tt;
    tt.a =1,tt.b =1;
    t.a =A,t.b =B;
    t.yuefen() ;
    
    for(int i=3;;i++)
    {
        mx=i;
        dfs(t,1,tt);
        if(ok)
        {
            for(int j=1;j<=mx;j++)
                printf("%lld ",ans[j]);
            return 0;
        }
    }
    
    return 0;
}

 5>魔板

单点修改,单个数字,还是没有字符串好用

#include<cstdio>
#include<cstdlib>
#include<string>
#include<map>
#include<queue>
#include<iostream>
using namespace std;
string ed;
map <string ,int > step;
struct node
{    
    string s,op;
    int stp; 
}st;
queue <node> q;

void A(node nw)
{
    string t;
    for(int i=7;i>=0;i--)
        t+=nw.s[i] ;
    if(step[t]>0) return ;
    
    if(t==ed)
    {
        cout<<nw.stp <<endl;
        cout<<nw.op +'A'<<endl;
        exit(0);
    }
    
    node tt;
    step[t] = tt.stp =nw.stp +1;
    tt.s =t ,tt.op = nw.op +'A';
    q.push(tt); 
}
void B(node nw)
{
    string t;
    t+=nw.s[3] ;
    for(int i=0;i<3;i++) t+=nw.s[i] ;
    for(int i=5;i<8;i++) t+=nw.s[i] ;
    t+=nw.s[4] ;
    if(step[t]>0) return ;
    
    if(t==ed)
    {
        cout<<nw.stp <<endl;
        cout<<nw.op +'B'<<endl;
        exit(0);
    }
    
    node tt;
    step[t] = tt.stp =nw.stp +1;
    tt.s =t ,tt.op = nw.op +'B';
    q.push(tt); 
}
void C(node nw)
{
    string t=nw.s ;
    char c=t[1];
    t[1]=t[6];
    t[6]=t[5];
    t[5]=t[2];
    t[2]=c;
    if(step[t]>0) return ;
    
    if(t==ed)
    {
        cout<<nw.stp <<endl;
        cout<<nw.op +'C'<<endl;
        exit(0);
    }
    
    node tt;
    step[t] = tt.stp =nw.stp +1;
    tt.s =t ,tt.op = nw.op +'C';
    q.push(tt); 
}

int main ()
{
    
    int x;
    for(int i=0;i<8;i++)
        scanf("%d",&x),ed +=(char)('0'+x);
    for(int i=1;i<=8;i++)
        st.s +=(char)('0'+i);
    if(st.s ==ed)
    {
        printf("0
");
        return 0;
    }
    
    st.stp =1;
    step[st.s ] =1;
    q.push(st); 
    while(!q.empty() )
    {
        node ttt=q.front();
        q.pop() ;
        A(ttt);
        B(ttt);
        C(ttt);
    }
    
    return 0;
}

6>knight moves

用了双端搜索,两个queue

940ms->240ms

#include<cstdio>
#include<cstdlib>
#include<queue>
#include<cstring>
using namespace std;
int l,T;
const int N=303;

struct node
{
    int x,y;
    node(int xx,int yy)
    { x=xx,y=yy; }
    node(){}
};
queue <node> q1,q2;
int sz1,sz2;
int stp1[N][N],stp2[N][N];

int py[8][2]={{1,2},{2,1},{-1,2},{2,-1},{1,-2},{-2,1},{-1,-2},{-2,-1}};
bool ok;
void bfs()
{
    while(sz1>sz2) 
    {
        node nw=q1.front() ;
        q1.pop() ,sz1--;
        for(int i=0;i<8;i++)
        {
            int nx=nw.x +py[i][0];
            int ny=nw.y +py[i][1];
            if(nx<0 || ny<0 || nx>=l || ny>=l || stp1[nx][ny] ) continue;
            if(stp2[nx][ny])
            {
                printf("%d
",stp2[nx][ny]+stp1[nw.x ][nw.y ]-1);
                ok=true;
                return ;
            }
            
            stp1[nx][ny]=stp1[nw.x ][nw.y ]+1;
            q1.push(node(nx,ny)),sz1++; 
        }
    }
    while(sz1<=sz2)
    {
        node nw=q2.front() ;
        q2.pop() ,sz2--;
        for(int i=0;i<8;i++)
        {
            int nx=nw.x +py[i][0];
            int ny=nw.y +py[i][1];
            if(nx<0 || ny<0 || nx>=l || ny>=l || stp2[nx][ny] ) continue;
            if(stp1[nx][ny])
            {
                printf("%d
",stp1[nx][ny]+stp2[nw.x ][nw.y ]-1);
                ok=true;
                return ;
            }
            
            stp2[nx][ny]=stp2[nw.x ][nw.y ]+1;
            q2.push(node(nx,ny)),sz2++; 
        }
    }
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(stp1,0,sizeof(stp1));
        memset(stp2,0,sizeof(stp2));
        while(!q1.empty() ) q1.pop() ;
        while(!q2.empty() ) q2.pop() ;
        
        scanf("%d",&l);
        node t;
        scanf("%d%d",&t.x ,&t.y );
        q1.push(t),stp1[t.x ][t.y ]++ ;
        scanf("%d%d",&t.x ,&t.y ); 
        q2.push(t),stp2[t.x ][t.y ]=-1 ; 
        sz1=sz2=1;
        
        if(stp1[t.x ][t.y ] ==1)
        {
            printf("0
");
            continue;
        }
        
        ok=false;
        while((sz1 || sz2 )&& !ok)
            bfs();
    }
    
    return 0;
}

 7>拯救大兵瑞恩

好坑的状态设计,不过还好一遍过了

#include<cstdio>
#include<cstdlib>
#include<bitset>
#include<queue>
using namespace std;
int n,m,p;
const int mx_state=(1<<10)+3,M=13;
int py[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int wall[M][M][5],key[M][M];

int step[M][M][mx_state];
struct node
{
    int x,y,state;
    node(int xx,int yy,int ss)
    {x=xx,y=yy,state=ss;}
    node(){}
};
queue <node> q;

int get_fx(int a,int b)
{
    if(a==1 && !b) return 0;
    if(a==-1 &&!b) return 2;
    if(b==1 && !a) return 1;
    if(b==-1 &&!a) return 3;
}
void bfs()
{
    step[1][1][0]=1;
    q.push(node(1,1,0));
    while(!q.empty() )
    {
        node nw=q.front();q.pop();
        
        for(int i=0;i<4;i++)
        {
            if(wall[nw.x ][nw.y ][i]==-1 || (wall[nw.x ][nw.y ][i] & nw.state ) !=wall[nw.x ][nw.y ][i] ) continue;
            int nx=nw.x +py[i][0];
            int ny=nw.y +py[i][1];
            int st=nw.state | key[nx][ny];
            
            if(!nx || !ny || nx>n || ny>m || step[nx][ny][st] ) continue;
            step[nx][ny][st] =step[nw.x ][nw.y ][nw.state ] +1;
            if(nx==n && ny==m )
            {
                printf("%d
",step[nx][ny][st] -1);
                exit(0);
            }
            else q.push(node(nx,ny,st ) );
        }
    }
    
}

int main()
{
    scanf("%d%d%d",&n,&m,&p);
    int t,xi,yi,x2,y2,fx,ky;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d%d",&xi,&yi,&x2,&y2,&ky);
        fx=get_fx(x2-xi,y2-yi);
        if(!ky) 
            wall[xi][yi][fx]=wall[x2][y2][(fx+2)%4]=-1;
        else 
            wall[xi][yi][fx] |=(1<<ky),
            wall[x2][y2][(fx+2)%4] |=(1<<ky);
    }
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&xi,&yi,&ky);
        key[xi][yi]|=(1<<ky);
    }
    
    bfs();
    printf("-1
");
    
    return 0;
}

8>weight

一道很绕的搜索题

已知原数列 a1,a2,,an 中的前 1项,前 2 项,前3项, ⋯ ,前 n 项的和,

以及后 1 项,后 2 项,后 3 项, ⋯ ,后 n 项的和,但是所有的数都被打乱了顺序。

此外,我们还知道数列中的数存在于集合 S 中。试求原数列。当存在多组可能的数列时,求字典序最小的数列。

直接枚举前n-1个和,求数列...

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,nn,sum;
const int N=1003;
int m,st[N];
int a[N<<1],d[N],pre_sum[N],bac_sum[N];

bool check(int nw,int p1,int p2)
{
    for(int i=p1;i<n;i++) pre_sum[i]=pre_sum[i-1]+d[i];
    for(int i=p2;i>0;i--) bac_sum[i]=bac_sum[i+1]+d[i];
    
    while(p1<n && p2>0)
    {
        if(p1<n && pre_sum[p1]==a[nw]) p1++,nw++;
        else if(p2>0 && bac_sum[p2]==a[nw]) p2--,nw++;
        else return false;
    }
    return true;
}
void print()
{
    for(int i=1;i<=n;i++) printf("%d ",d[i]);
    exit(0);
}

void dfs(int nw,int p1,int p2)
{
    if(p1==p2)
    {
        d[p1]=sum-pre_sum[p1-1]-bac_sum[p2+1];
        int pos=lower_bound(st+1,st+m+1,d[p1])-st;
        if(st[pos]!=d[p1]) return ;
        if(check(nw,p1,p2))
            print();
        return ;
    }
    
    d[p1]=a[nw]-pre_sum[p1-1];
    pre_sum[p1]=a[nw];
    int pos=lower_bound(st+1,st+m+1,d[p1])-st;
    if(st[pos]==d[p1])
        dfs(nw+1,p1+1,p2);
    
    d[p2]=a[nw]-bac_sum[p2+1];
    bac_sum[p2]=a[nw];
    pos=lower_bound(st+1,st+m+1,d[p2])-st;
    if(st[pos]==d[p2])
        dfs(nw+1,p1,p2-1);
}

int main()
{
    scanf("%d",&n);
    nn=n<<1;
    for(int i=1;i<=nn;i++) scanf("%d",&a[i]);
    sort(a+1,a+nn+1);
    scanf("%d",&m);
    for(int i=1;i<=m;i++) scanf("%d",&st[i]);
    sort(st+1,st+m+1);
    
    sum=a[nn];
    pre_sum[1]=d[1]=a[1];
    dfs(2,2,n);
    
    return 0;
}
原文地址:https://www.cnblogs.com/xwww666666/p/11433609.html