『一本通』深搜的剪枝技巧

数的划分

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,k,ans;
 4 
 5 void dfs(int x,int s,int lst) {
 6     if(x==0&&s==0) {ans++; return ;}
 7     if(s==0||x<s) return ;
 8     for(int i=lst;i<=x;i++) dfs(x-i,s-1,i);
 9 }
10 
11 int main() {
12     scanf("%d%d",&n,&k);
13     dfs(n,k,1);
14     printf("%d",ans);
15 }

 

Addition Chains

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,flag,ans[105];
 4 void print() {
 5     for(int i=1;i<m;i++) printf("%d ",ans[i]);
 6     printf("%d\n",ans[m]);
 7 }
 8 
 9 void dfs(int step,int lst) { //lst:上一次取的外层循环的数+1
10     if(step==m+1) {
11         if(ans[m]==n) flag=1;
12         return ;
13     }
14     for(int i=step-1;i>=lst;i--) //倒序枚举 
15      for(int j=i;j>=1;j--) {
16          ans[step]=ans[i]+ans[j];
17          int s=ans[step];
18          for(int k=step+1;k<=m;k++) s*=2; //计算当前形势下到第m项可得的最大值
19          if(s<n) break; 
20          if(ans[step]<=n) dfs(step+1,i+1);
21          if(flag) return;
22      }
23 }
24 
25 int main() {
26     ans[1]=1;
27     while(scanf("%d",&n)) {
28         if(!n) break;
29         if(n==1) {puts("1"); continue;}
30         for(flag=0,m=2;m<=n;m++) { //m枚举数列长度 
31             dfs(2,1);
32             if(flag) {print(); break;} 
33         }
34     }
35 }

 

平板涂色

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,ans,ind[15],vis[15],m[15][15];
 4 struct node{int x1,y1,x2,y2,cor;}a[15];
 5 inline int read() {
 6     int x=0; char c=getchar();
 7     while(c<'0'||c>'9') c=getchar();
 8     while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
 9     return x;
10 }
11 
12 void YU() {
13     ans=n;
14     for(int i=1;i<=n;i++)
15      for(int j=1;j<=n;j++)
16       if(a[i].x2==a[j].x1&&!(a[i].y2<a[j].y1||a[i].y1>a[j].y2))
17        ind[j]++,m[i][j]=1; //j矩形必须在i矩形涂色后,才能涂色
18 }
19 //目前涂色矩形的个数、刷子的颜色和拿起刷子的次数
20 void dfs(int num,int color,int cnt) { 
21     if(ans<=cnt) return ;
22     if(num==n) ans=cnt;
23     for(int i=1;i<=n;i++)
24      if(!vis[i]&&!ind[i]) {
25          vis[i]=1;
26          for(int j=1;j<=n;j++)
27           if(m[i][j]) ind[j]--;
28          if(a[i].cor==color) dfs(num+1,color,cnt);
29          else dfs(num+1,a[i].cor,cnt+1);
30          vis[i]=0;
31          for(int j=1;j<=n;j++)
32           if(m[i][j]) ind[j]++;
33      }
34 }
35 
36 int main() {
37     n=read();
38     for(int i=1;i<=n;i++)
39      a[i]=(node){read(),read(),read(),read(),read()};
40     YU(); //预处理
41     dfs(0,0,0);
42     printf("%d",ans);
43 }
44 //DFS(拓扑排序)

 

生日蛋糕

 1 #include<bits/stdc++.h>
 2 #define INF 1e5
 3 using namespace std;
 4 int n,m,ans,a[21],b[21];
 5 //已用体积、已有表面积、当前层数、上一层的半径和高 
 6 void dfs(int v,int s,int p,int r,int h) {
 7     if(p==0) {
 8         if(v==n) ans=min(ans,s);
 9         return ;
10     }
11     if(s+a[p-1]>=ans) return ; 
12     if(v+b[p-1]>n) return ;
13     if(s+2*(n-v)/r>=ans) return ; //已有表面积+余下侧面积>=当前最优值 
14     for(int i=r-1;i>=p;i--) { //倒序枚举半径 
15         if(p==m) s=i*i;
16         int H=min((n-v-b[p-1])/(i*i),h-1);
17         for(int j=H;j>=p;j--) //倒序枚举高 
18          dfs(v+i*i*j,s+2*i*j,p-1,i,j);
19     }
20 }
21 
22 int main() {
23     scanf("%d%d",&n,&m);
24     ans=INF;
25     for(int i=1;i<=20;i++) {
26         a[i]=a[i-1]+2*i*i; //第1~i层的最小表面积 
27         b[i]=b[i-1]+i*i*i; //第1~i层的最小体积 
28     }
29     dfs(0,0,m,n+1,n+1);
30     if(ans==INF) puts("0");
31     else printf("%d",ans);
32 }

 

weight

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,sum,a[2005],ans[1005];
 4 bool S[505];
 5 inline int read() {
 6     int x=0; char c=getchar();
 7     while(c<'0'||c>'9') c=getchar();
 8     while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
 9     return x;
10 } 
11  
12 void dfs(int l,int r,int sl,int sr,int k) {
13     if(l==r) { //最后一个数 
14         if(sum-sl-sr<=500&&S[sum-sl-sr]) {
15            ans[l]=sum-sl-sr;  
16            for(int i=1;i<=n;i++) printf("%d ",ans[i]);
17            exit(0);
18         } return ;
19     }
20     if(a[k]-sl<=500&&S[a[k]-sl]) { //a[k]为前缀和 
21         ans[l]=a[k]-sl;
22         dfs(l+1,r,a[k],sr,k+1);
23     }
24     if(a[k]-sr<=500&&S[a[k]-sr]) { //a[k]为后缀和 
25         ans[r]=a[k]-sr;
26         dfs(l,r-1,sl,a[k],k+1);
27     }
28 }
29 
30 int main() {
31     n=read();
32     for(int i=1;i<=2*n;i++) a[i]=read();
33     m=read();
34     for(int i=1;i<=m;i++) S[read()]=1;
35     sort(a+1,a+2*n+1); 
36     sum=a[2*n]; //sum:原数列各数之和 
37     dfs(1,n,0,0,1);
38 }

 

小木棍

此题优化较多,详见题解传送门

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,sum,len,tot,a[65],vis[65];
 4 int cmp(int x,int y) {return x>y;}
 5 //正在拼接第k根原始木棍,还要拼接now的长度,上次使用的是p-1长的小木棍 
 6 bool dfs(int k,int now,int p) {
 7     if(k==tot+1) return 1;
 8     if(now==0) return dfs(k+1,len,1); //拼接下一根原始木棍 
 9     for(int i=p;i<=n;i++) //从p开始枚举(之前的已经搜过) 
10      if(!vis[i]&&a[i]<=now) {
11          vis[i]=1; //标记此木棍已使用 
12          if(dfs(k,now-a[i],i+1)) return 1;
13          vis[i]=0;
14          if(now==len||now==a[i]) return 0; //此木棍无法使用 
15          while(a[i]==a[i+1]) i++; //避免使用相同长度的木棍 
16      }
17     return 0;
18 }
19 int main() {
20     scanf("%d",&n);
21     for(int i=1;i<=n;i++) {
22         scanf("%d",&a[i]);
23         if(a[i]>50) i--,n--; 
24         else sum+=a[i]; //sum统计所有小木棍长度之和 
25     }
26     sort(a+1,a+n+1,cmp); //将小木棍按长度从大到小排序(短的木棍后期拼接更灵活)
27     for(int i=a[1];i<=sum;i++) //原始木棍的长度大于等于小木棍中最长的一根(a[1]) 
28      if(sum%i==0) { //原始木棍的长度肯定是sum的约数 
29          len=i,tot=sum/i;
30          if(dfs(1,len,1)) {printf("%d",i); return 0;}
31      }
32 } 

 

靶型数独

思路来自jolionvin

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int ans,a[10][10],b[85];
 4 bool flag,vis[3][10][10];
 5 struct node{int h,zero;}row[10];
 6 //按每行中0的个数从小到大排序 
 7 int cmp(node x1,node x2) {return x1.zero<x2.zero;}
 8 
 9 int Gong(int x,int y) { //获取(x,y)所在的小九宫格 
10     int A=x%3==0?x/3:x/3+1;
11     int B=y%3==0?y/3:y/3+1;
12     return (A-1)*3+B;
13 }
14 
15 int F(int x,int y) { //获取(x,y)对应的分数
16     if(x==1||y==1||x==9||y==9) return 6;
17     if(x==2||y==2||x==8||y==8) return 7;
18     if(x==3||y==3||x==7||y==7) return 8;
19     if(x==4||y==4||x==6||y==6) return 9;
20     return 10;
21 }
22 
23 int Score() { //当前数字组合下的总分数 
24     int tot=0;
25     for(int i=1;i<=9;i++)
26      for(int j=1;j<=9;j++) tot+=F(i,j)*a[i][j];
27     return tot;
28 }
29 
30 void dfs(int id) { //id:b数组的编号 
31     if(id==82) {
32         flag=1,ans=max(ans,Score());
33         return ;
34     }
35     int x=b[id]/9+1,y=b[id]%9; //当前编号对应的行和列 
36     if(y==0) x--,y=9;
37     if(a[x][y]!=0) {dfs(id+1); return ;} //此方格已经被填入数字 
38     int z=Gong(x,y);
39     for(int i=1;i<=9;i++) {
40         //判断当前i是否能够使用 
41         if(vis[0][x][i]||vis[1][y][i]||vis[2][z][i]) continue;
42         a[x][y]=i;
43         vis[0][x][i]=vis[1][y][i]=vis[2][z][i]=1;
44         dfs(id+1);
45         a[x][y]=0;
46         vis[0][x][i]=vis[1][y][i]=vis[2][z][i]=0;
47     }
48 }
49 
50 int main() {
51     for(int i=1;i<=9;i++) {
52         int cnt=0; //cnt:记录该行0的个数
53         for(int j=1;j<=9;j++) {
54             scanf("%d",&a[i][j]);
55             if(a[i][j]!=0) {
56                 int h=a[i][j],g=Gong(i,j);
57                 vis[0][i][h]=vis[1][j][h]=vis[2][g][h]=1;
58             } //已经填好的数,所在的行/列/小九宫格均不可再使用该数 
59             else cnt++;
60         }
61         row[i].h=i,row[i].zero=cnt;
62     }
63     sort(row+1,row+10,cmp); //优先安排0少的行
64     int tot=0;
65     for(int i=1;i<=9;i++) //存储待搜索格子的优先顺序
66      for(int j=1;j<=9;j++) b[++tot]=(row[i].h-1)*9+j; 
67     dfs(1);
68     if(!flag) puts("-1");
69     else printf("%d",ans);  
70 }

 

埃及分数

分母的范围

$\frac{1}{i}\le\frac{x}{y}$,所以 $i\ge\frac{y}{x}$

$\frac{x}{y}/\frac{1}{i}\le len-tot+1$,所以 $i\le\frac{y}{x}(len-tot+1)$

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 using namespace std;
 4 int a,b,len;
 5 LL Min=1e9,s[105],ans[105];
 6 
 7 void dfs(int k,LL lst,LL x,LL y) {
 8     if(k>len) {
 9         if(!x&&s[len]<Min) {
10             for(int i=1;i<=len;i++) ans[i]=s[i];
11             Min=s[len];
12         } return ;
13     }
14     LL l=max(lst+1,y/x),r=y/x*(len-k+1);
15     for(LL i=l;i<=r;i++) {
16         LL nx=x*i-y,ny=y*i;
17         if(nx<0) continue;
18         s[k]=i;
19         dfs(k+1,i,nx,ny);
20     }
21 }
22 
23 int main() {
24     scanf("%d%d",&a,&b);
25     for(len=2;;len++) {
26         dfs(1,1,a,b);
27         if(Min<1e9) break;
28     }
29     for(int i=1;i<=len;i++) 
30      printf("%lld ",ans[i]);
31 }

 

posted @ 2019-01-26 10:50  YeLingqi  阅读(468)  评论(0编辑  收藏  举报