搜索专题总结

搜索专题总结

第七章的例题做得差不多了,还有一道枚举二叉树和一道比较难的搜方块的没过,另外有一道火柴的用IDA*水过,并没有过大数据,由于这道可以用dancing links过,所以留着dancing links一坑。接下来总结下这章的收获,首先最重要的当然是不需要判重的高效率的IDA*以及估价函数的设计技巧;然后是bfs+hash写得更熟练了,如果hash需要erase那么就只能用指针版的,但是效率会很慢,否则就用数组版的。

做搜索题的几个要点:

1,估算最坏复杂度。

2,寻找合适的剪枝策略,估计剪枝的效果,根据情况选择IDA*还是bfs,最坏复杂度高但剪枝效果明显的优先IDA*。

3,想清楚代码的大致框架。

4,想清楚代码实现困难的细节以及可能出错需要注意的地方。

5,大胆快速的写吧。

下面是第七章的例题:

A题:

输出所有的xxxxx / xxxxx =N

只要枚举上面的数字就行了,复杂度<10^5,直接循环枚举。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=(1<<29);
const double EPS=0.0000001;
const double Pi=acos(-1.0);

int n;
int a,b;
bool vis[maxn];
bool has[20];

bool check(int b)
{
    MS0(has);
    REP(i,1,5){
        int x=b%10;b/=10;
        if(vis[x]||has[x]) return 0;
        has[x]=1;
    }
    return 1;
}

int main()
{
    //freopen("in.txt","r",stdin);
    int st=1;
    while(cin>>n,n){
        if(!st) puts("");
        st=0;
        MS0(vis);
        a=0;
        bool flag=0;
        REP(i,0,9){
            vis[i]=1;
            a=a*10+i;
            REP(j,0,9){
                if(vis[j]) continue;
                vis[j]=1;
                a=a*10+j;
                REP(k,0,9){
                    if(vis[k]) continue;
                    vis[k]=1;
                    a=a*10+k;
                    REP(l,0,9){
                        if(vis[l]) continue;
                        vis[l]=1;
                        a=a*10+l;
                        REP(x,0,9){
                            if(vis[x]) continue;
                            vis[x]=1;
                            a=a*10+x;
                            if(a%n==0&&check(a/n)){
                                flag=1;
                                printf("%05d / %05d = %d\n",a,a/n,n);
                            }
                            vis[x]=0;
                            a-=x;a/=10;
                        }
                        vis[l]=0;
                        a-=l;a/=10;
                    }
                    vis[k]=0;
                    a-=k;a/=10;
                }
                vis[j]=0;
                a-=j;a/=10;
            }
            vis[i]=0;
            a-=i;a/=10;
        }
        if(!flag) printf("There are no solutions for %d.\n",n);
    }
    return 0;
}

B题:

水题,暴力。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=(1<<29);
const double EPS=0.0000001;
const double Pi=acos(-1.0);

int n;
ll a[maxn];

ll cal(int l,int r)
{
    ll res=1;
    REP(i,l,r) res*=a[i];
    return res;
}

int main()
{
    int casen=1;
    while(cin>>n){
        REP(i,1,n) scanf("%lld",&a[i]);
        ll ans=0;
        REP(l,1,n){
            REP(r,l,n){
                ll tmp=cal(l,r);
                ans=max(ans,tmp);
            }
        }
        printf("Case #%d: The maximum product is %lld.\n\n",casen++,ans);
    }
    return 0;
}

C题:

水题,先确定下范围再枚举。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=(1<<29);
const double EPS=0.0000001;
const double Pi=acos(-1.0);

ll k,x,y;
struct St
{
    ll x,y;
};
vector<St> ans;

int main()
{
    while(cin>>k){
        ans.clear();
        for(y=k+1;;y++){
            x=k*y/(y-k);
            if(x<y) break;
            if((k*y)%(y-k)==0){
                x=k*y/(y-k);
                if(x<y) break;
                ans.push_back({x,y});
            }
        }
        printf("%d\n",(int)ans.size());
        for(int i=0;i<ans.size();i++){
            printf("1/%lld = 1/%lld + 1/%lld\n",k,ans[i].x,ans[i].y);
        }
    }
    return 0;
}

D题:

水题,dfs就行了。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=(1<<29);
const double EPS=0.0000001;
const double Pi=acos(-1.0);

int n,a[maxn];
bool vis[maxn],isprime[maxn];

void getPrime()
{
    memset(isprime,1,sizeof(isprime));
    isprime[1]=0;
    REP(i,2,maxn-1){
        if(!isprime[i]) continue;
        for(int j=i+i;j<maxn;j+=i) isprime[j]=0;
    }
}

void dfs(int cur)
{
    if(cur>n){
        if(!isprime[a[n]+a[1]]) return;
        REP(i,1,n) printf("%d%c",a[i],i==n?‘\n‘:‘ ‘);
        return;
    }
    REP(i,1,n){
        if(!vis[i]&&isprime[a[cur-1]+i]){
            a[cur]=i;
            vis[i]=1;
            dfs(cur+1);
            vis[i]=0;
        }
    }
}

int main()
{
    int casen=1;
    getPrime();
    int st=1;
    while(cin>>n){
        MS0(vis);
        if(!st) puts("");
        st=0;
        printf("Case %d:\n",casen++);
        a[1]=1;
        vis[1]=1;
        dfs(2);
    }
    return 0;
}

E题:困难的串。

很经典的题,只要不断往后添加然后判断就行了,关键在于判断的复杂度,由于substr(0,l-1)是已经判断过的,所以只要判断后缀就行了。

/// 19:43 2015/11/16
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=(1<<29);

int n,L;
int cur;
bool flag;
string ans;

bool judge(string s)
{
    int len=s.size();
    for(int i=1;i<=len/2;i++){
        //out<<s.substr(len-2*i,i)<<"_"<<s.substr(len-i,i)<<endl;
        if(s.substr(len-2*i,i)==s.substr(len-i,i)) return false;
    }
    return true;
}

void dfs(string s)
{
    if(flag) return;
    if(cur==n){
        ans=s;flag=1;return;
    }
    cur++;
    REP(i,0,L-1){
        string t=s+char(‘A‘+i);
        if(judge(t)) dfs(t);
    }
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(cin>>n>>L,(n||L)){
        cur=0;flag=0;
        dfs("");
        REP(i,0,(int)ans.size()-1){
            printf("%c",ans[i]);
            if(i%64==63&&i!=(int)ans.size()-1) printf("\n");
            else if(i%4==3&&i!=(int)ans.size()-1) printf(" ");
        }
        printf("\n%d\n",(int)ans.size());
    }
    return 0;
}

F题:

枚举排列就行了,复杂度能过,注意输入的处理。

/// 20:34~ 2015/11/16
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=(1<<29);

char s[maxn];
vector<int> G[maxn];
int a[maxn],pos[maxn];
int b[maxn],ans;
int id[maxn],fid[maxn];
int n;
bool vis[maxn];

void build()
{
    REP(i,1,8) G[i].clear();
    memset(id,-1,sizeof(id));
    n=0;
    int len=strlen(s);
    for(int i=0;i<len;){
        int u=id[s[i]];
        if(u==-1) u=id[s[i]]=++n,fid[n]=s[i];
        int j;
        for(j=i+2;j<len;j++){
            if(s[j]==‘;‘) break;
            int v=id[s[j]];
            if(v==-1) v=id[s[j]]=++n,fid[n]=s[j];
            G[u].push_back(v);
            G[v].push_back(u);
        }
        i=j+1;
    }
}

void update()
{
    REP(i,1,n) pos[a[i]]=i;
    int res=0;
    REP(u,1,n){
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i];
            res=max(res,abs(pos[u]-pos[v]));
        }
    }
    if(res<ans){
        REP(i,1,n) b[i]=a[i];
        ans=res;
    }
    else if(res==ans){
        bool flag=0;
        REP(i,1,n){
            if(fid[a[i]]<fid[b[i]]){
                flag=1;break;
            }
            else if(fid[a[i]]>fid[b[i]]) break;
        }
        if(flag){
            REP(i,1,n) b[i]=a[i];
        }
    }
}

void dfs(int cur,int now)
{
    a[cur]=now;
    vis[now]=1;
    if(cur==n) update();
    REP(i,1,n) if(!vis[i]) dfs(cur+1,i);
    vis[now]=0;
}

void solve()
{
    ans=INF;
    REP(i,1,n) dfs(1,i);
    REP(i,1,n) printf("%c ",fid[b[i]]);
    printf("-> %d\n",ans);
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(~scanf("%s",s)){
        if(strcmp(s,"#")==0) break;
        build();
        solve();
    }
    return 0;
}

H题:

倒水。bfs都可以,注意由于求的是倒水量最小而不是次数最少,所以bfs要用优先队列,IDA*似乎不太适合,因为倒水量的范围比较大。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=8000100;
const int INF=(1<<29);

int s[3],d;
int ans,d_;
bool vis[maxn];
struct Node
{
    int s[3];
    int step;
    friend bool operator<(Node A,Node B)
    {
        return A.step>B.step;
    }
    void debug()
    {
        printf("x=%2d y=%2d z=%2d step=%2d\n",s[0],s[1],s[2],step);
    }
};

int st(Node u)
{
    int res=0;
    REP(i,0,2) res=res*201+u.s[i];
    return res;
}

Node pop(Node u,int x,int y)
{
    if(u.s[x]<=s[y]-u.s[y]){
        u.step+=u.s[x];
        u.s[y]+=u.s[x];
        u.s[x]=0;
        return u;
    }
    else{
        u.step+=s[y]-u.s[y];
        u.s[x]-=s[y]-u.s[y];
        u.s[y]=s[y];
        return u;
    }
}

void bfs()
{
    MS0(vis);
    priority_queue<Node> q;
    Node ss={0,0,s[2],0};
    q.push(ss);
    vis[st(ss)]=1;
    ans=0;d_=0;
    while(!q.empty()){
        Node u=q.top();q.pop();
        ///u.debug();
        REP(i,0,2){
            if(u.s[i]>d_&&u.s[i]<=d){
                d_=u.s[i];ans=u.step;
            }
        }
        if(d_==d) break;
        Node v;
        /// 1->2
        v=pop(u,0,1);

        if(!vis[st(v)]) q.push(v),vis[st(v)]=1;
        /// 1->3
        v=pop(u,0,2);
        if(!vis[st(v)]) q.push(v),vis[st(v)]=1;
        /// 2->1
        v=pop(u,1,0);
        if(!vis[st(v)]) q.push(v),vis[st(v)]=1;
        /// 2->3
        v=pop(u,1,2);
        if(!vis[st(v)]) q.push(v),vis[st(v)]=1;
        /// 3->1
        v=pop(u,2,0);//cout<<"01"<<" ";v.debug();
        if(!vis[st(v)]) q.push(v),vis[st(v)]=1;
        /// 3->2
        v=pop(u,2,1);
        if(!vis[st(v)]) q.push(v),vis[st(v)]=1;
    }
}

int main()
{
    //freopen("in.txt","r",stdin);
    int T;cin>>T;
    while(T--){
        scanf("%d%d%d%d",&s[0],&s[1],&s[2],&d);
        MS0(vis);
        bfs();
        printf("%d %d\n",ans,d_);
    }
    return 0;
}

I题:

很经典的状态空间搜索问题,由于不可走的区域很多,所以重新建图以减小复杂度,这里判重很容易,直接bfs,当然IDA*也可以。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=(1<<29);

int w,h,m;
char ch[20][20];
vector<int> G[maxn];int n;
struct Node
{
    char c;
    int x;
    friend bool operator<(Node A,Node B)
    {
        return A.c<B.c;
    }
};
Node S[maxn],T[maxn];
int id[20][20];
int dx[]={-1,1,0,0};
int dy[]={0,0,-1,1};
bool vis[300][300][300];
struct qNode
{
    int x,y,z;
    int d;
};

bool jud(int x,int y,int z,int nx,int ny,int nz)
{
    if(m==1) return 1;
    if(m==2){
        if(nx==ny) return 0;
        if(x==ny&&y==nx) return 0;
        return 1;
    }
    if(m==3){
        if(nx==ny||ny==nz||nz==nx) return 0;
        if(x==ny&&y==nx) return 0;
        if(y==nz&&z==ny) return 0;
        if(z==nx&&x==nz) return 0;
        return 1;
    }
}

int bfs()
{
    MS0(vis);
    queue<qNode> q;
    qNode s={S[1].x,S[2].x,S[3].x,0};
    qNode t={T[1].x,T[2].x,T[3].x,0};
    q.push(s);
    vis[s.x][s.y][s.z]=1;
    while(!q.empty()){
        qNode u=q.front();q.pop();
        int x=u.x,y=u.y,z=u.z,d=u.d;
        if(x==t.x&&y==t.y&&z==t.z) return d;
        for(int i=0;i<G[x].size();i++){
            int nx=G[x][i];
            for(int j=0;j<G[y].size();j++){
                int ny=G[y][j];
                for(int k=0;k<G[z].size();k++){
                    int nz=G[z][k];
                    if(!vis[nx][ny][nz]&&jud(x,y,z,nx,ny,nz)){
                        q.push({nx,ny,nz,d+1});
                        vis[nx][ny][nz]=1;
                    }
                }
            }
        }
    }
    return -1;
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(cin>>w>>h>>m){
        if(w==0&&h==0&&m==0) break;
        gets(ch[0]);
        REP(i,1,h) gets(ch[i]+1);
        n=0;
        MS0(id);
        int ks=0,kt=0;
        MS0(S);MS0(T);
        REP(i,1,h){
            REP(j,1,w){
                if(ch[i][j]!=‘#‘){
                    id[i][j]=++n;
                    if(islower(ch[i][j])) S[++ks]={ch[i][j],n};
                    if(isupper(ch[i][j])) T[++kt]={ch[i][j],n};
                }
            }
        }
        REP(i,0,maxn-1) G[i].clear();
        G[0].push_back(0);
        REP(i,1,h){
            REP(j,1,w){
                int u=id[i][j];
                if(!u) continue;
                G[u].push_back(u);
                REP(k,0,3){
                    int ni=i+dx[k],nj=j+dy[k];
                    int v=id[ni][nj];
                    if(!v) continue;
                    G[u].push_back(v);
                }
            }
        }
        sort(S+1,S+m+1);sort(T+1,T+m+1);
        printf("%d\n",bfs());
    }
    return 0;
}

J题:剪纸问题。

又是一道很经典的状态空间搜索问题。这里以每个元素的相对位置来设计的估价函数剪枝效果明显,所以用IDA*,效率非常高。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))
#define PB push_back

using namespace std;

typedef long long ll;
const int maxn=2000100;
const int INF=(1<<29);

int n;
struct Node
{
    int a[11];
    void read()
    {
        REP(i,1,n) scanf("%d",&a[i]);
    }
};Node s;

bool judge(Node s)
{
    REP(i,1,n){
        if(s.a[i]!=i) return 0;
    }
    return 1;
}

Node cut(Node s,int l,int r,int L,int R)
{
    Node res=s;
    REP(i,0,R-L) res.a[l+i]=s.a[L+i];
    int lt=l+R-L+1;
    REP(i,0,r-l) res.a[lt+i]=s.a[l+i];
    return res;
}

int Not(Node s)
{
    int res=0;
    REP(i,1,n-1) res+=(s.a[i]+1!=s.a[i+1]);
    return res;
}

int dfs(Node s,int cur,int maxd)
{
    if(cur==maxd){
        if(judge(s)) return 1;
        return 0;
    }
    if(cur*3+Not(s)>maxd*3) return 0;
    int res=0;
    REP(l,1,n-1){
        REP(r,l,n-1){
            REP(R,r+1,n){
                Node ns=cut(s,l,r,r+1,R);
                res|=dfs(ns,cur+1,maxd);
                if(res) return 1;
            }
        }
    }
    return res;
}

int main()
{
    //freopen("in.txt","r",stdin);
    int casen=1;
    while(cin>>n,n){
        s.read();
        int ans=INF;
        REP(i,0,n){
            if(dfs(s,0,i)){
                ans=i;break;
            }
        }
        printf("Case %d: %d\n",casen++,ans);
    }
    return 0;
}

K题:

又是一道经典的状态空间搜索问题。这里如果用bfs的话要分三次,而IDA*依旧优势明显,效率高,代码短,注意每一步只能改变一个,所以估价函数为cur+不一样的个数>maxd,这里不一样的个数取最大就可以了,就不用分三次了。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=(1<<29);

struct State
{
    int a[24];
};State s;
string str;
int ans,x;
int A[]={0,2,6,11,15,20,22};
int B[]={1,3,8,12,17,21,23};
int C[]={4,5,6,7,8,9,10};
int D[]={13,14,15,16,17,18,19};
int Y[]={6,7,8,11,12,15,16,17};

/*
    0   1
    2   3
4 5 6 7 8 9 10
    11  12
3 4 5 6 7 8 19
    20  21
    22  23
*/

void debug(State s)
{
    printf("      ");printf("%2d    %2d\n",s.a[0],s.a[1]);
    printf("      ");printf("%2d    %2d\n",s.a[2],s.a[3]);
    REP(i,4,10) printf("%2d ",s.a[i]);printf("\n");
    printf("      ");printf("%2d    %2d\n",s.a[11],s.a[12]);
    REP(i,13,19) printf("%2d ",s.a[i]);printf("\n");
    printf("      ");printf("%2d    %2d\n",s.a[20],s.a[21]);
    printf("      ");printf("%2d    %2d\n",s.a[22],s.a[23]);
    cout<<endl;
}

State mov(State s,int *A,bool tag)
{
    if(tag){
        int tmp=s.a[A[0]];
        REP(i,1,6) s.a[A[i-1]]=s.a[A[i]];
        s.a[A[6]]=tmp;
        return s;
    }
    else{
        int tmp=s.a[A[6]];
        for(int i=6;i>=1;i--) s.a[A[i]]=s.a[A[i-1]];
        s.a[A[0]]=tmp;
        return s;
    }
}

State go(State s,char ch)
{
    State ns=s;
    if(ch==‘A‘) ns=mov(s,A,1);
    if(ch==‘B‘) ns=mov(s,B,1);
    if(ch==‘C‘) ns=mov(s,C,0);
    if(ch==‘D‘) ns=mov(s,D,0);
    if(ch==‘E‘) ns=mov(s,B,0);
    if(ch==‘F‘) ns=mov(s,A,0);
    if(ch==‘G‘) ns=mov(s,D,1);
    if(ch==‘H‘) ns=mov(s,C,1);
    return ns;
}

int judge(State s)
{
    REP(i,1,7) if(s.a[Y[i]]!=s.a[Y[0]]) return 0;
    return s.a[Y[0]];
}

int Not(State s)
{
    int cnt[4]={0};
    REP(i,0,7){
        cnt[s.a[Y[i]]]++;
    }
    return max(cnt[1],max(cnt[2],cnt[3]));
}

bool dfs(State s,int cur,int maxd,string road)
{

    if(cur==maxd){
        int xx=judge(s);
        if(xx){
            str=road;
            x=xx;
            //debug(s);
            return 1;
        }
        return 0;
    }
    if(cur+8-Not(s)>maxd) return 0;
    for(char ch=‘A‘;ch<=‘H‘;ch++){
        State ns=go(s,ch);
        if(dfs(ns,cur+1,maxd,road+ch)) return 1;
    }
    return 0;
}

int main()
{
    //freopen("in.txt","r",stdin);
    int st;
    while(cin>>st){
        if(st==0) return 0;
        s.a[0]=st;
        REP(i,1,23) scanf("%d",&s.a[i]);
        //debug(s);debug(mov(s,C,0));
        for(int i=0;;i++){
            if(dfs(s,0,i,"")){
                ans=i;break;
            }
        }
        if(ans==0) puts("No moves needed");
        else cout<<str<<endl;
        printf("%d\n",x);
    }
}

L题:

这应该是很经典的贪心。。。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=(1<<29);

ll N,s1,v1,s2,v2;

ll solve1(ll N,ll s1,ll v1,ll s2,ll v2)
{
    ll res=0;
    REP(i,0,N/s1){
        ll tmp=i*v1+((N-i*s1)/s2)*v2;
        res=max(tmp,res);
    }
    return res;
}

ll solve2(ll N,ll s1,ll v1,ll s2,ll v2)
{
    //  a*s1=b*s2->v1>v2   v1/s1>v2/s2 -> v1*s2>v2*s1
    if(v1*s2<v2*s1){
        swap(s1,s2);swap(v1,v2);
    }
    ll d=__gcd(s1,s2);
    ll a=s2/d,b=s1/d;
    ll res=0;
    REP(i,0,b-1){
        ll tmp=i*v2+((N-i*s2)/s1)*v1;
        res=max(res,tmp);
    }
    return res;
}

int main()
{
    //freopen("in.txt","r",stdin);
    int T;cin>>T;
    int casen=1;
    while(T--){
        scanf("%lld%lld%lld%lld%lld",&N,&s1,&v1,&s2,&v2);
        ll ans=0;
        if(N/s1<maxn) ans=solve1(N,s1,v1,s2,v2);
        else if(N/s2<maxn) ans=solve1(N,s2,v2,s1,v1);
        else ans=solve2(N,s1,v1,s2,v2);
        printf("Case #%d: %lld\n",casen++,ans);
    }
    return 0;
}

O题:

这题大数据没过却莫名其妙地AC了。。。用的是IDA*,看来如果拼人品的话就IDA*吧。。。由于这题的另一种解法是dancing links,有时间学dancing links的时候一定回来看这题。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=(1<<29);

int n,k;
struct State
{
    bool has[61];
    int cnt;
    int c[61];
    int sum_c,max_c;
    int Cnt()
    {
        int res=0;
        REP(d,1,n){
            REP(r,0,n-d){
                REP(c,1,n-d+1){
                    int x=1;
                    int st=r*(2*n+1)+c;
                    REP(j,0,d-1) x&=(has[st+j]&has[st+n+j*(2*n+1)]&has[st+n+d+j*(2*n+1)]&has[st+d*(2*n+1)+j]);
                    res+=x;
                }
            }
        }
        return res;
    }
    void cal_c()
    {
        MS0(c);max_c=0;sum_c=0;
        REP(i,1,2*n*(n+1)){
            if(has[i]){
                has[i]=0;
                int tmp=cnt;
                cnt=Cnt();
                c[i]=tmp-cnt;
                max_c=max(c[i],max_c);
                sum_c+=c[i];
                has[i]=1;
                cnt=tmp;
            }
        }
    }
};State s;

bool dfs(State s,int cur,int maxd)
{
    s.cal_c();
    if(cur==maxd) return s.cnt==0;
    if(cur+s.cnt-s.sum_c>maxd) return 0;
    REP(i,1,2*n*(n+1)){
        if(s.has[i]){
            if(s.c[i]==s.max_c){
                State ns=s;
                ns.has[i]=0;
                ns.cnt=s.cnt-s.c[i];
                if(dfs(ns,cur+1,maxd)) return 1;
            }
        }
    }
    return 0;
}

int main()
{
    //freopen("in.txt","r",stdin);
    int T;cin>>T;
    while(T--){
        scanf("%d%d",&n,&k);
        REP(i,1,2*n*(n+1)) s.has[i]=1;
        REP(i,1,k){
            int x;scanf("%d",&x);
            s.has[x]=0;
        }
        s.cnt=s.Cnt();
        s.cal_c();
        int ans=INF;
        REP(i,1,60){
            if(dfs(s,0,i)){
                ans=i;break;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

时间: 2024-11-10 10:34:20

搜索专题总结的相关文章

搜索专题小结及例题:POJ2251&amp;POJ1426&amp;POJ3087&amp;POJ2488

图的遍历也称为搜索,就是从图中某个顶点出发,沿着一些边遍历图中所有的顶点,且每个顶点仅被访问一次,遍历可采取两种不同的方式:深度优先搜索(DFS)和广度优先搜索(BFS). 1.DFS算法思想` 从顶点v出发深度遍历图G的算法 ① 访问v0顶点,置vis[v0]=1,搜索v0未被访问的邻接点w,若存在邻接点w,则dfs(w),直到到达所有邻接点都被访问过的顶点u为止,接着退回一步,看是否还有其他没有被访问的邻接点.如果有,则访问此顶点,进行前述类似的访问,如果没有,就在退回一步进行搜索,重复上述

搜索专题

搜索专题 标签(空格分隔): ACM专题小结 搜索专题主要就是围绕dfs()和bfs(). 这两个函数其实不难,搜索可以解决很多最小路径,最少次数问题bfs()等问题 维数从一维到三维:主要抓住移动的方向的控制,以及对访问过的状态的标记,以免重复访问,重复访问一方面运行时间加长另一方面申请的空间也会无厘头的暴增. 曾经在刷一道题的时候,系统显示的使用空间远远大于定义变量的空间,一时间懵逼了,后来才发现是标记没处理好,导致了大量的重复访问. 还有一个要注意的地方就是队列的清空,或者在函数里面定义队

CSP2019突击训练(搜索专题)

专题一 简单搜索POJ 1321 棋盘问题POJ 2251 Dungeon MasterPOJ 3278 Catch That CowPOJ 3279 FliptilePOJ 1426 Find The MultiplePOJ 3126 Prime PathPOJ 3087 Shuffle'm UpPOJ 3414 PotsFZU 2150 Fire GameUVA 11624 Fire!POJ 3984 迷宫问题HDU 1241 Oil DepositsHDU 1495 非常可乐HDU 261

‘简单’搜索专题总结

kuangbin专题一 A. 棋盘问题 在是棋盘的格子上放置棋子,其中要求所有棋子不同行也不同列,求摆放的方案数. dfs,参数:行.棋子数,遍历的时候要回溯. B. Dungeon Master 走迷宫,3D的走迷宫. C. Catch That Cow 最短时间找到那只牛. bfs,剪枝. D. FilpTile 翻方块,上一行状态决定下一行的翻转. E. Find The Multiple 大胆的bfs,数据范围很小的. F. Prime Path bfs G. Shuffle'm Up

记忆化搜索专题

Hdoj  1078 题目足迹 题意: n*n的矩阵,从(0,0)出发,每个点上有一个数值,每次只能水平或竖直的走至多k个格子,要求总的路线上的数值是严格单增的,并且使总的和最大. 代码: 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #define mem(a,b) memset(a,b,sizeof(a)) 6 #define for

搜索专题小结:迭代加深搜索

迭代加深搜索 迭代加深搜索(Iterative Deepening Depth-First Search, IDDFS)经常用于理论上解答树深度上没有上界的问题,这类问题通常要求出满足某些条件时的解即可.比如在"埃及分数"问题中要求将一个分数a/b分解成为若干个形如1/d的加数之和,而且加数越少越好,如果加数个数相同,那么最小的分数越大越好.下面总结一下该方法的一般流程: (1)概述:迭代加深搜索是通过限制每次dfs的最大深度进行的搜索.令maxd表示最大的搜索深度,那么dfs就只能在

poj 3126 Prime Path(搜索专题)

Prime Path Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 20237   Accepted: 11282 Description The ministers of the cabinet were quite upset by the message from the Chief of Security stating that they would all have to change the four-di

搜索专题(不定期更新)

1.POJ 2386  Lake Counting 题意:给出一块区域,询问有多少个湖泊? 思路:DFS,对于'W',深搜一次,并标记已访问.之后每次对未访问的'W'做一次深搜. 1 #include<iostream> 2 #include<memory.h> 3 using namespace std; 4 char lake[105][105]; 5 bool vis[105][105]; 6 int n, m; 7 void DFS(int r, int c) 8 { 9

暴力搜索专题小结:全排列及可重集排列生成算法

1~n的全排列 (1)思路:按照递归的思想,初始化集合S中含有1~n所有元素.如果1~n的集合S为空,那么输出全排列:否则从小到大依次考虑每个元素i,在A的末尾添加i后,集合S变为S-{i}.这里我们不需要集合S,只需要利用一个变量cur表示当前位要填的数即可.那么A中没有出现过的元素均可以选择. #define N 100 int A[N]; void print_permutation(int n, int*A, int cur) { if (cur == n) { for (int i =