搜索专题总结

搜索专题总结

第七章的例题做得差不多了,还有一道枚举二叉树和一道比较难的搜方块的没过,另外有一道火柴的用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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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);
    }
}
View Code

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;
}
View Code

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;
}
View Code

 

posted @ 2015-11-23 11:36  __560  阅读(262)  评论(0编辑  收藏  举报