『一本通』深搜的剪枝技巧
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 }
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 }
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 }
如有错误请指正。