这题数据比较水,直接暴力是能过的,但是却可以做的更优。
<1>有两个行之有效且易于运用的剪枝。
这里写代码片#include
#include
using namespace std;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
int a[20][20];
int f[20][20];
int dep, sum, n, m, ans = 0;
void dfs(int de, int x, int y, int k){
if(k + k == sum) {
ans = de;//更新已知最优解
}
if(ans && de > ans || k * 2 > sum){//符合上面两点就回溯
return;
}
for(int i = 0; i < 4; i++){
int xx = x + dx[i];
int yy = y + dy[i];
if(xx < 1 || yy < 1 || xx > n || yy > m || f[xx][yy] == 1) continue;
f[xx][yy] = 1;
dfs(de + 1, xx, yy, k + a[xx][yy]);
f[xx][yy] = 0;
}
}
int main(){
scanf("%d%d", &m, &n);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
scanf("%d", &a[i][j]);
sum += a[i][j];
}
}
dfs(1, 1, 1, a[1][1]);
printf("%d\n", ans);
return 0;
}
<2>迭代加深
这两点优化可以让程序比暴力提高成千上万倍速度。但是还有一些不可避免的浪费。比如说,假设正解是x,那么这两点不可避免的回搜索到第x层,x+1层……我们知道搜索依托的是树形结构,树的每一层之间的差距很大,我们搜索x+1层可能要消耗第x层几倍的时间。
我们可以用迭代加深搜索。枚举递归深度,如果在当前深度可以找到答案的话就直接输出。
枚举深度的时候,会重复搜索原来的深度,比如深度枚举到4的时候,必然会走一遍深度为3的路子。但是因为每层之间节点数目差距较大,可以忽略。
所以,看起来迭代加深是集合了深搜与广搜的优点的
#include
#include
using namespace std;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
int a[20][20];
int f[20][20];
int dep, sum, n, m;
int dfs(int de, int x, int y, int k){
if(k + k == sum) {
return 1;
}
if(de > dep || k * 2 > sum){
return 0;
}
for(int i = 0; i < 4; i++){
int xx = x + dx[i];
int yy = y + dy[i];
if(xx < 1 || yy < 1 || xx > n || yy > m || f[xx][yy] == 1) continue;
f[xx][yy] = 1;
if(dfs(de + 1, xx, yy, k + a[x][y])) return 1;
f[xx][yy] = 0;
}
return 0;
}
int main(){
scanf("%d%d", &m, &n);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
scanf("%d", &a[i][j]);
sum += a[i][j];
}
}
for(dep = 1; dep < n * m; dep++){
memset(f, 0, sizeof(f));
if(dfs(1, 1, 1, 0)){
printf("%d\n", dep);
break;
}
}
if(dep == n*m) puts("0");
return 0;
}