搜索Ⅰ

搜索剪枝

今天来了一拨各种搜索,非常的晕啊!搜索还是有些时候非常的困难!今天我们的重点还是各种搜索剪枝,下面分析一下今天做的例题!

先来一道热身题:

这道题是一道大爆搜,需要的技巧其实还是比较的少,就是说我们的,如果你是裸的爆搜,那肯定是不行的,我们的爆搜还是需要一些技巧,就是每次从每行未知数少的地方开始搜索,可以一定程度上减少一些多余的状态,然后我们把未知点排序,从状态少的排到状态多的,依次进行搜索,然后我们搜索判定条件就是,如果当前数字填入该点,行上和列上和九宫格中都没有出现那个数字,那么就可以填入,然后标记数字已被使用,继续往下搜索,记得回溯!!!

双倍经验题目:P1784 数独

AC code:

/*
    Name: P1074 靶形数独
    Copyright: njc
    Author: Mudrobot
    Date: 2018/10/24 12:13:27
    Description: Search
*/
#include<bits/stdc++.h>
#define gc() getchar()//caution!!!
#define LL long long

using namespace std;
/*inline char gc() {
  static char buf[1<<18],*fs,*ft;
  return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<18,stdin)),fs==ft)?EOF:*fs++;
}*/
template<class T>
inline void read(T &aa) {
  register int k=0,f=1;
  register char c=gc();
  for (;!isdigit(c);c=gc()) if(c=='-')f=-1;
  for (;isdigit(c);c=gc()) k=(k<<3)+(k<<1)+(c-'0');
  aa=k*f;
}
template<class T>
inline void out(T x){if(x>9)out(x/10);putchar(x%10+'0');}
int a[10][10],line[10][10],col[10][10],cube[10][10],have,score=-1,sii;
struct ssd{
    int x,y,mark,loc;
    ssd(){}
    ssd(int a,int b,int c,int d){
        x=a;y=b;mark=c;loc=d;
    }
};
struct sd{
    int sum,id;
}zero[10];
vector<ssd> p;
int point(int,int);
int locate(int,int);
bool cmp(sd a,sd b){return a.sum<b.sum;}
void dfs(int dep,int now){
    if(dep==sii){
        score=max(score,now);
    }
    else{
        for(int i=9;i>=1;--i){
            if(!line[p[dep].x][i]&&!col[p[dep].y][i]&&!cube[p[dep].loc][i]){
                line[p[dep].x][i]=col[p[dep].y][i]=cube[p[dep].loc][i]=1;
                dfs(dep+1,now+i*p[dep].mark);
                line[p[dep].x][i]=col[p[dep].y][i]=cube[p[dep].loc][i]=0;
            }
        }
    }
}
int main()
{
    //freopen(".in", "r", stdin);freopen(".out", "w", stdout);
    for(int i=1;i<=9;++i){
        zero[i].id=i;
        for(int j=1;j<=9;++j){
            read(a[i][j]);
            if(a[i][j]>0) line[i][a[i][j]]=col[j][a[i][j]]=cube[locate(i,j)][a[i][j]]=1,have+=point(i,j)*a[i][j];
            else zero[i].sum++;
        }
    }
    sort(zero+1,zero+10,cmp);
    for(int i=1;i<=9;++i){
        for(int j=1;j<=9;++j){
            if(a[zero[i].id][j]==0)
                p.push_back(ssd(zero[i].id,j,point(zero[i].id,j),locate(zero[i].id,j)));
        }
    }
    sii=p.size();dfs(0,have);
    printf("%d",score);
    //fclose(stdin);fclose(stdout);
    return 0;
}
int locate(int x,int y){
    if(1<=x&&x<=3){
        if(1<=y&&y<=3) return 1;
        if(4<=y&&y<=6) return 2;
        if(7<=y&&y<=9) return 3;
    }
    if(4<=x&&x<=6){
        if(1<=y&&y<=3) return 4;
        if(4<=y&&y<=6) return 5;
        if(7<=y&&y<=9) return 6;
    }
    if(7<=x&&x<=9){
        if(1<=y&&y<=3) return 7;
        if(4<=y&&y<=6) return 8;
        if(7<=y&&y<=9) return 9;
    }
}
int point(int x,int y){
    if(x==1||x==9||y==1||y==9) return 6;
    if(x==2||x==8||y==2||y==8) return 7;
    if(x==3||x==7||y==3||y==7) return 8;
    if(x==4||x==6||y==4||y==6) return 9;
    if(x==5) return 10;
}
/*
7 0 0 9 0 0 0 0 1 
1 0 0 0 0 5 9 0 0 
0 0 0 2 0 0 0 8 0 
0 0 5 0 2 0 0 0 3 
0 0 0 0 0 0 6 4 8 
4 1 3 0 0 0 0 0 0 
0 0 7 0 0 2 0 9 0 
2 0 1 0 6 0 8 0 4 
0 8 0 5 0 4 0 1 2
*/

数独代码:

/*
    Name: P1784 数独
    Copyright: njc
    Author: Mudrobot
    Date: 2018/10/24 18:21:04
    Description: Soduku+Search
*/
#include<bits/stdc++.h>
#define gc() getchar()//caution!!!
#define LL long long
#define N 11
using namespace std;
/*inline char gc() {
  static char buf[1<<18],*fs,*ft;
  return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<18,stdin)),fs==ft)?EOF:*fs++;
}*/
template<class T>
inline void read(T &aa) {
  register T k=0,f=1;
  register char c=gc();
  for (;!isdigit(c);c=gc()) if(c=='-')f=-1;
  for (;isdigit(c);c=gc()) k=(k<<3)+(k<<1)+(c-'0');
  aa=k*f;
}
template<class T>
inline void out(T x){if(x>9)out(x/10);putchar(x%10+'0');}
bool line[N][N],lie[N][N],cube[N][N];
int num[N][N],sii;
struct ssd{
    int sum,id;
}l[N];
struct sd{
    int x,y,loc;
    sd(){}
    sd(int a,int b,int c){
        x=a;y=b;loc=c;
    }
};
vector<sd> p;
int locate(int x,int y){
    if(1<=x&&x<=3){
        if(1<=y&&y<=3) return 1;
        if(4<=y&&y<=6) return 2;
        if(7<=y&&y<=9) return 3;
    }
    if(4<=x&&x<=6){
        if(1<=y&&y<=3) return 4;
        if(4<=y&&y<=6) return 5;
        if(7<=y&&y<=9) return 6;
    }
    if(7<=x&&x<=9){
        if(1<=y&&y<=3) return 7;
        if(4<=y&&y<=6) return 8;
        if(7<=y&&y<=9) return 9;
    }
}
bool cmp(ssd a,ssd b){
    return a.sum<b.sum;
}
void dfs(int dep){
    if(dep==sii){
        for(int i=1;i<=9;++i){
            for(int j=1;j<=9;++j){
                printf("%d ",num[i][j]);
            }
            printf("
");
        }
    }
    else{
        for(int i=1;i<=9;++i){
            if(!line[p[dep].x][i]&&!lie[p[dep].y][i]&&!cube[p[dep].loc][i]){
                line[p[dep].x][i]=lie[p[dep].y][i]=cube[p[dep].loc][i]=true;
                num[p[dep].x][p[dep].y]=i;
                dfs(dep+1);
                line[p[dep].x][i]=lie[p[dep].y][i]=cube[p[dep].loc][i]=false;
                num[p[dep].x][p[dep].y]=0;
            }
        }
    }
}
int main()
{
    //freopen(".in", "r", stdin);freopen(".out", "w", stdout);
    for(int i=1;i<=9;++i){
    	for(int j=1;j<=9;++j){
    		read(num[i][j]);
    		if(num[i][j]){
    			line[i][num[i][j]]=lie[j][num[i][j]]=cube[locate(i,j)][num[i][j]]=true;
    		}
    		else l[i].sum++;
    		l[i].id=i;
    	}
    }
    sort(l+1,l+10,cmp);
    for(int i=1;i<=9;++i){
    	int u=l[i].id;
    	for(int j=1;j<=9;++j){
    		if(!num[u][j]){
    			p.push_back(sd(u,j,locate(u,j)));
    		}
    	}
    }
    sii=p.size();
    dfs(0);
    //fclose(stdin);fclose(stdout);
    return 0;
}
/*
8 0 0 0 0 0 0 0 0 
0 0 3 6 0 0 0 0 0 
0 7 0 0 9 0 2 0 0 
0 5 0 0 0 7 0 0 0 
0 0 0 0 4 5 7 0 0 
0 0 0 1 0 0 0 3 0 
0 0 1 0 0 0 0 6 8 
0 0 8 5 0 0 0 1 0 
0 9 0 0 0 0 4 0 0
*/

下面开始我们的重头戏:

(color{red} ext {【解题思路】})

这道题的方法是深搜+剪枝,这里搜索部分的细节不用说,我们主要来讲一讲一些剪枝方面的优化。

以下摘自《算法竞赛·进阶指南》

1、优化搜索顺序

把木棍长度从大到小排序优先尝试较长的木棍。

2、排除等效冗余

(1)可以限制先后加入一根原始木棒的木棍长度是递减的。这是因为先拼上一根长度为x的木棍,再拼上一根长为y的木棍(x<y)与先拼上y再拼上是等效的,只需要搜索其中的一种即可。

(2)对于当前原始木棒,记录最近的一次尝试拼接木棒的长度,如果分支搜索失败回溯,不再尝试向该木棒中拼接其他相同长度的木棍(必定也会失败)

(3)如果我们尝试的是一根还没有拼的木棒,那么如果拼入的第一根的递归分支就返回失败,那么可以直接剪掉当前的状态。

(4)如果当前我们尝试的木棒恰好可以拼完上一根木棒,但是后面的递归分支返回结果为失败,那么我们可以直接剪掉当前状态。这个可以使用贪心进行解释,因为再用1根木棍恰好拼完当前原始木棒必然比再用若干根木棍拼完当前原始木棒更好!

AC code:

/*
    Name: P1120 小木棍 [数据加强版]
    Copyright: njc
    Author: Mudrobot
    Date: 2018/10/24 19:34:22
    Description: Search
*/
#include<bits/stdc++.h>
#define gc() getchar()//caution!!!
#define LL long long
#define N 70
using namespace std;
/*inline char gc() {
  static char buf[1<<18],*fs,*ft;
  return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<18,stdin)),fs==ft)?EOF:*fs++;
}*/
template<class T>
inline void read(T &aa) {
  register T k=0,f=1;
  register char c=gc();
  for (;!isdigit(c);c=gc()) if(c=='-')f=-1;
  for (;isdigit(c);c=gc()) k=(k<<3)+(k<<1)+(c-'0');
  aa=k*f;
}
template<class T>
inline void out(T x){if(x>9)out(x/10);putchar(x%10+'0');}
int m,sum,stk[N],n,maxx,cnt,len;
bool vis[N];
bool cmp(int a,int b){return a>b;}
bool dfs(int u,int now,int loc){
    if(u>cnt) return true;
    if(now==len) return dfs(u+1,0,1);
    int fail=0;
    for(int i=loc;i<=n;++i){
        if(!vis[i]&&stk[i]+now<=len&&fail!=stk[i]){
            vis[i]=true;
            if(dfs(u,stk[i]+now,i+1)) return true;
            else{
                fail=stk[i];vis[i]=false;
                if(now==0||now+stk[i]==len) return false;
            }
        }
    }
    return false;
}
int main()
{
    //freopen(".in", "r", stdin);freopen(".out", "w", stdout);
    read(m);int a;
    for(int i=1;i<=m;++i){
        read(a);
        if(a<=50) stk[++n]=a,sum+=a,maxx=max(maxx,a);
    }
    sort(stk+1,stk+1+n,cmp);
    for(int i=maxx;i<=sum;++i){
        if(sum%i) continue;
        cnt=sum/i;len=i;
        memset(vis,false,sizeof(vis));
        if(dfs(1,0,1)){printf("%d",i);return 0;}
    }
    printf("%d",sum);
    //fclose(stdin);fclose(stdout);
    return 0;
}
/*
9
5 2 1 5 2 1 5 2 1
*/

这道题就相对来说比较复杂了,所以说我们在这里就简要叙述一下剪枝。剩余的实现细节,大家可以在代码中体会一下!

可行性剪枝

我们可以先预处理出从上到下前i层的最小体积和侧面积。(当第i层的半径分别取1,2,3···,i,高度取1,2,3···,i时,有最小体积与侧面积)

如果当前的体积v加上1~dep-1层的最小体积大于N,就可以剪枝。

同理如果当前体积v加上1~dep-1层的最大体积小于N,就可以剪枝。(最大体积我们使用函数进行处理)

再同理,如果我们当前表面积s加上1~dep-1的最小侧面积大于已经搜到的答案,剪枝!

然后这道题就愉快的解决了!

/*
    Name: P1731 [NOI1999]生日蛋糕
    Copyright: njc
    Author: Mudrobot
    Date: 2018/10/24 20:47:07
    Description: Search
*/
#include<bits/stdc++.h>
#define gc() getchar()//caution!!!
#define LL long long
#define INF 28742394
using namespace std;
/*inline char gc() {
  static char buf[1<<18],*fs,*ft;
  return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<18,stdin)),fs==ft)?EOF:*fs++;
}*/
template<class T>
inline void read(T &aa) {
  register T k=0,f=1;
  register char c=gc();
  for (;!isdigit(c);c=gc()) if(c=='-')f=-1;
  for (;isdigit(c);c=gc()) k=(k<<3)+(k<<1)+(c-'0');
  aa=k*f;
}
template<class T>
inline void out(T x){if(x>9)out(x/10);putchar(x%10+'0');}
int n,m,mincake[33],minn=INF;
int maxcake(int dep,int nr,int nh){
    int ans=0,i;
    for(i=dep,nr--,nh--;i<=m;i++,nr--,nh--){
        ans+=nr*nr*nh;
        if(ans>=500000||nr*nr*nh>500000) return 500000;
    }
    return ans;
}
void dfs(int dep,int nr,int nh,int v,int s){
    if(s>minn) return;
    if(v+mincake[m-dep+1]>n||v+maxcake(dep,nr,nh)<n) return;
    if(dep==m){
        for(int r=nr-1;r>=1;--r){
            if((n-v)%(r*r)) continue;
            int h=(n-v)/(r*r);
            if(h>=nh) continue;
            minn=min(minn,s+2*r*h+(m==1?r*r:0));
        }
    }
    else{
        for(int r=nr-1;r>=m-dep+1;--r){
            for(int h=min(nh-1,(int)((n-v)/(r*r)));h>=m-dep+1;--h){
                if(dep==1) dfs(2,r,h,r*r*h,r*r+2*r*h);
                else dfs(dep+1,r,h,r*r*h+v,s+2*r*h);
            }
        }
    }
}
int main()
{
    //freopen(".in", "r", stdin);freopen(".out", "w", stdout);
    read(n);read(m);
    for(int i=1;i<=m;++i){
        mincake[i]=mincake[i-1]+i*i*i;
    }
    dfs(1,101,10001,0,0);
    if(minn==INF) printf("0
");
    else printf("%d
",minn);
    //fclose(stdin);fclose(stdout);
    return 0;
}
/*
100
2
*/
原文地址:https://www.cnblogs.com/mudrobot/p/13328961.html