dfs之剪枝与优化
dfs之剪枝与优化
166. 数独

样例输入:
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end
样例输出:
417369825632158947958724316825437169791586432346912758289643571573291684164875293
416837529982465371735129468571298643293746185864351297647913852359682714128574936
代码模板:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 9;
int lowbit(int x){
return x&(-x);
}
int col[N],row[N],cell[N][N];
char str[90];
int map[1<<N],ones[1<<N];
void init(){
//将行和列和方格中得状态都设为可填状态 0为不可填 1为可填
for(int i=0;i<N;i++) col[i]=row[i]=(1<<N)-1;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
cell[i][j]=(1<<N)-1;
}
int getones(int x,int y){
//活得行列方格中所有1(可填)的交集
return col[y]&row[x]&cell[x/3][y/3];
}
bool dfs(int cnt){
if(!cnt) return true;
//找到可填充元素最少的位置 -> 优化搜索顺序
int minn=10,x,y;
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(ones[getones(i,j)]<minn&&str[i*9+j]=='.'){
minn=ones[getones(i,j)];
x=i,y=j;
}
//开始填充数字
for(int i=getones(x,y);i;i-=lowbit(i)){ //枚举所有1(可填)
int t=map[lowbit(i)]; //获得低位1的十进制数 map获得低位1的位置
row[x]-=(1<<t);
col[y]-=(1<<t);
cell[x/3][y/3]-=(1<<t);
str[x*9+y]='1'+t;
if(dfs(cnt-1)) return true;
//回溯
row[x]+=(1<<t);
col[y]+=(1<<t);
cell[x/3][y/3]+=(1<<t);
str[x*9+y]='.';
}
return false;
}
int main(){
//预处理某状态是第几位
for(int i=0;i<N;i++) map[1<<i]=i;
//预处理所有状态为1(可填)的个数
for(int i=0;i<1<<N;i++){
int s=0;
for(int j=i;j;j-=lowbit(j)) s++;
ones[i]=s;
}
while(cin>>str,str[0]!='e'){
init(); //初始化
int cnt=N*N;
//将题目中已经填得数字初始化行列方格
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(str[i*9+j]!='.'){
int t=str[i*9+j]-'1';
row[i]-=1<<t;
col[j]-=1<<t;
cell[i/3][j/3]-=1<<t;
cnt--; //填方格-1
}
dfs(cnt);
cout<<str<<endl;
}
return 0;
}
167. 木棒

样例输入:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
样例输出:
6
5
代码模板:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 64;
int n,sum,length;
int sticks[N];
bool st[N];
//u当前已经有多少根木棒 cur当前木棒的长度 start当前从哪根木棍开始枚举(木棍从大到小枚举)
bool dfs(int u,int cur,int start){
//当前的木棒数量等于所有木棍的总和->当前木棒长度可行
if(u*length==sum) return true;
//当前的木棒长度可行 开始递归下一根木棒
if(cur==length) return dfs(u+1,0,0);
//从大到小枚举木棍
for(int i=start;i<n;i++){
if(st[i]) continue;
int l=sticks[i];
if(cur+l>length) continue;
st[i]=true;
if(dfs(u,cur+l,i+1)) return true;
st[i]=false;
//如果是木棒的第一个木棍失败,则一定失败
if(!cur) return false;
//如果是该木棒的最后一根木棒时失败时,说明前面的排法方案有问题,直接return
if(cur+l==length) return false;
// 跳过相同的木棒
int j=i;
while(j<n && sticks[j]==l) j++;
i=j-1;
}
return false;
}
int main(){
while(cin>>n,n){
sum=0,length=0;
for(int i=0;i<n;i++){
int l;
cin>>l;
sticks[i]=l;
sum+=l;
length=max(length,l);
}
//降序排序->优化搜索顺序->排除等效冗余
sort(sticks,sticks+n);
reverse(sticks,sticks+n);
memset(st,false,sizeof st);
for(int i=0;i<n;i++)
if(sticks[i]>50)
st[i]=true;
while(true){
//枚举的木棒的长度必须被sum整除
if(sum%length==0&&dfs(0,0,0)){
cout<<length<<endl;
break;
}
length++;
}
}
return 0;
}
168. 生日蛋糕





思路:
1.首先看到题目知道暴搜,暴力枚举r和h,由于体积公式中r的影响最大所以先枚举r再枚举h,但是r和h有范围 最小:当前层数;最大 (1)剩下的体积根据公式当高等于1时候r取得最大值 (2) 下一层的半径+1。
2.当剩下的体积小于可安排的最小体积,return;当前表面积加上可安排的最小表面积大于ans,return;
3.根据表面积和体积公式求得关系剪枝:当前已经安排表面积得加上(...大于)ans,return(最恶心且最重要得一环)
样例输入:
100
2
样例输出:
68
代码模板:
#include <bits/stdc++.h>
using namespace std;
const int M = 30;
int n, m, ans = 0x3f3f3f3f;
int minv[M], mins[M], R[M], H[M];
//自底向上递归
//v当前处理的体积和 s当前处理的面积和
void dfs(int dep, int v, int s)
{
if (v + minv[dep] > n) return;
if (s + mins[dep] >= ans) return; // 剪枝(4)
if (s + 2 * (n - v) / R[dep + 1] >= ans) return; // 剪枝(5)
if(dep == 0)
{
if (v == n) ans = min(ans, s);
return;
}
// 剪枝(2)(3)
for (int r = min(R[dep + 1] - 1, (int)sqrt(n - v)); r >= dep; r -- )
for (int h = min(H[dep + 1] - 1, (n - v) / r / r); h >= dep; h -- )
{
R[dep] = r, H[dep] = h;
dfs(dep - 1, v + r * r * h, s + 2 * r * h + (dep == m ? r * r : 0));
}
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i ++ )
{
minv[i] = minv[i - 1] + i * i * i;
mins[i] = mins[i - 1] + 2 * i * i;
}
R[m + 1] = H[m + 1] = 0x3f3f3f3f;
dfs(m, 0, 0); // 剪枝(1)
if (ans == 0x3f3f3f3f) puts("0");
else printf("%d\n", ans);
return 0;
}

浙公网安备 33010602011771号