八中 搜索作业

P1074 [NOIP 2009 提高组] 靶形数独

思路:

学会通过clock卡时技巧,
clock() 本身是 系统调用,常数不小
每1000次dfs查看一下clock是否超时

int maxt=CLOCKS_PER_SEC*97/100,t;

void dfs(){
	cnt++;
	if (cnt==1000) cnt=0;
	if (cnt == 0 && clock() - t > maxt)
	{
		cout << ans << "\n";
		exit(0);
	}
	......
}

然后这道题目数据比较坑,从上往下搜索用暴搜会超时,从下往上就不会。。。

暴搜代码如下, 复杂度 \(O(9^m)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=9;
int a[N][N],sc[N][N],ans,cnt;
int maxt=CLOCKS_PER_SEC*97/100,t;
bool vis[3][3][10],row[9][10],col[9][10];
void init(){
	for(int i=0;i<9;i++)
		for(int j=0;j<9;j++)
			sc[i][j]=10-max(abs(i-4),abs(j-4));
	for(int i=0;i<9;i++){
		for(int j=0;j<9;j++){
			cin>>a[i][j];
			if(a[i][j]!=0){
				int x=a[i][j];
				vis[i/3][j/3][x]=1;
				row[i][x]=1;
				col[j][x]=1;
			}
		}
	}
	t=clock();
} 
void dfs(int x,int y,int sum){
	if(x==0&&y==8){
		ans=max(ans,sum);
        return;
	}
	int nx=x-(y+1)/9,ny=(y+1)%9;
	cnt++;
    if (cnt==1000) cnt=0;
	if(cnt==0&&clock()-t>=maxt){
		if(ans==0) cout<<-1;
		else cout<<ans<<endl;
		exit(0);
	}
//	cout<<nx<<' '<<ny<<endl;
	if(a[nx][ny]!=0){
		dfs(nx,ny,sum+sc[nx][ny]*a[nx][ny]);
		return ;
	}
	for(int i=1;i<=9;i++){
		if(vis[nx/3][ny/3][i]||row[nx][i]||col[ny][i]) continue;
		vis[nx/3][ny/3][i]=row[nx][i]=col[ny][i]=1;
		dfs(nx,ny,sum+sc[nx][ny]*i);
		vis[nx/3][ny/3][i]=row[nx][i]=col[ny][i]=0;
	}
}
int main(){
	init();
	dfs(9,8,0);
	if(ans==0){
		cout<<-1;
		return 0;
	}
	cout<<ans<<endl;
	return 0;
}

下载测试超时样例分析

输入

0 0 0 0 0 6 0 0 3
0 0 0 0 0 0 6 0 0
0 0 0 0 0 3 0 0 0
0 0 0 1 0 0 2 0 0
0 0 0 0 3 0 0 0 4
0 2 7 0 0 0 0 3 0
1 0 0 0 6 8 4 7 9
0 9 6 2 7 0 1 0 5
8 0 0 0 9 0 3 0 0

输出

2864

这个输入中左上区域(尤其前 3 行、前 6 列)几乎全是 0,约束极少。相当于没剪枝。

P1219 [USACO1.5] 八皇后 Checker Challenge

思路:

dfs 按行放皇后,并判断
column[col] : 某一列是否已有皇后
diag1[row-col+n] : 主对角线
diag2[row+col] : 副对角线
相当于生成全排列, 然后用对角线剪枝,复杂度 \(O(n!)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 13 + 5;

int n;
int pos[MAXN];                 // pos[i]:第 i 行皇后放在第几列
bool used_col[MAXN];           // 某一列是否被占用
bool used_diag1[2 * MAXN];     // 主对角线:row - col + n
bool used_diag2[2 * MAXN];     // 副对角线:row + col
long long total = 0;           // 解的总数

vector<vector<int>> first3;    // 保存前 3 个解

// 深度优先搜索,当前放到第 row 行
void dfs(int row) {
    // 如果已经成功放完 n 行,得到一个完整解
    if (row > n) {
        total++;
        // 只保存前 3 个解
        if ((int)first3.size() < 3) {
            vector<int> sol;
            for (int i = 1; i <= n; i++)
                sol.push_back(pos[i]);
            first3.push_back(sol);
        }
        return;
    }

    // 枚举当前行可以放的列(从小到大,保证字典序)
    for (int col = 1; col <= n; col++) {
        // 对角线编号计算
        int d1 = row - col + n;   // 主对角线
        int d2 = row + col;       // 副对角线

        // 若列或对角线冲突,则跳过
        if (used_col[col] || used_diag1[d1] || used_diag2[d2])
            continue;

        // 尝试放皇后
        pos[row] = col;
        used_col[col] = true;
        used_diag1[d1] = true;
        used_diag2[d2] = true;

        dfs(row + 1);

        // 回溯,撤销操作
        used_col[col] = false;
        used_diag1[d1] = false;
        used_diag2[d2] = false;
    }
}

int main() {
    cin >> n;

    dfs(1);

    // 输出前 3 个解
    for (auto &sol : first3) {
        for (int i = 0; i < n; i++) {
            cout << sol[i];
            if (i + 1 < n) cout << ' ';
        }
        cout << '\n';
    }

    // 输出解的总数
    cout << total << '\n';

    return 0;
}

P1101 单词方阵

思路:

DFS 用来“沿着一个方向往前走”,判断能否走完 7 个字符

为什么 DFS 不需要 vis 数组?
单词长度固定为 7
同一条 DFS 路径 不会回头, 不存在重复访问的问题
这是“定向 DFS”,不是迷宫搜索

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const string word = "yizhong";
int n;
char a[105][105];
bool ok[105][105];   // 标记是否属于 yizhong

// 8 个方向
int dx[8] = {-1,-1,-1, 0, 0, 1, 1, 1};
int dy[8] = {-1, 0, 1,-1, 1,-1, 0, 1};

/*
 dfs(x, y, dir, k)
 含义:
 当前在 (x, y)
 使用方向 dir
 已经匹配到 word[k]
*/
bool dfs(int x, int y, int dir, int k) {
    // 已经成功匹配完 "yizhong"
    if (k == 7) return true;

    // 越界
    if (x < 0 || x >= n || y < 0 || y >= n)
        return false;

    // 字符不匹配
    if (a[x][y] != word[k])
        return false;

    // 继续沿着 dir 方向前进
    int nx = x + dx[dir];
    int ny = y + dy[dir];
    return dfs(nx, ny, dir, k + 1);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }

    // 枚举起点
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {

            // yizhong 的首字母
            if (a[i][j] != 'y') continue;

            // 枚举 8 个方向
            for (int d = 0; d < 8; d++) {
                if (dfs(i, j, d, 0)) {
                    // 若成功,再走一遍路径进行标记
                    int x = i, y = j;
                    for (int k = 0; k < 7; k++) {
                        ok[x][y] = true;
                        x += dx[d];
                        y += dy[d];
                    }
                }
            }
        }
    }

    // 输出结果
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (ok[i][j]) cout << a[i][j];
            else cout << '*';
        }
        cout << '\n';
    }

    return 0;
}

posted @ 2025-12-15 11:57  katago  阅读(5)  评论(0)    收藏  举报