dfs简介
在图上寻找路径
int main() {
//将所有点都标记为新点;
//起点 = 1;
//终点 = 8;
cout << Dfs(起点);
return 0;
}
判断从V出发能否找到终点
bool Dfs(V) {//从V出发判断是否能到终点
if (V是终点)
return true;
if (V是旧点)
return false;
将V标记为旧点
对和V相邻的每个节点U{
if (Dfs(U) == true)
return true;
}
return false;
}
判断从V出发能否走到终点,如果能,要记录路径
Node path[MAX_LEN];//MAX_LEN取节点总数即可
int depth;
bool Dfs(V) {//从V出发进行遍历
if (V是终点) {
path[depth] = V;
return true;
}
if (V是旧点) {
return false;
}
将V标记为旧点;
path[depth] = V;
depth++;
对和V相邻的每个节点U{
if (Dfs(U) == true)
return true;
}
--depth;
return false;
}
int main() {
将所有点标记为新点;
depth = 0;
if (Dfs(起点)) {
for (int i = 0; i <= depth; i++)
cout << path[i] << endl;
}
return 0;
}
遍历图上所有节点
Dfs(V) {//从V出发进行遍历
if (V是旧点) {
return;
}
将V标记为旧点;
对和V相邻的每个点U{
Dfs(U);
}
}
int main() {
将所有点都标记为新点;
while (在图中能找到新点k) {
Dfs(k);
}
}
邻接矩阵
G[i][j]可以是自定义类型,可以是int,也可以是struct
邻接表
城堡问题
1817:城堡问题
总时间限制:
1000ms
内存限制:
65536kB
描述
1 2 3 4 5 6 7 ############################# 1 # | # | # | | # #####---#####---#---#####---# 2 # # | # # # # # #---#####---#####---#####---# 3 # | | # # # # # #---#########---#####---#---# 4 # # | | | | # # ############################# (图 1) # = Wall | = No wall - = No wall
图1是一个城堡的地形图。请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。城堡被分割成m*n(m≤50,n≤50)个方块,每个方块可以有0~4面墙。
输入
程序从标准输入设备读入数据。第一行是两个整数,分别是南北向、东西向的方块数。在接下来的输入行里,每个方块用一个数字(0≤p≤50)描述。用一个数字表示方块周围的墙,1表示西墙,2表示北墙,4表示东墙,8表示南墙。每个方块用代表其周围墙的数字之和表示。城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。输入的数据保证城堡至少有两个房间。
输出
城堡的房间数、城堡中最大房间所包括的方块数。结果显示在标准输出设备上。
样例输入
4 7 11 6 11 6 3 10 6 7 9 6 13 5 15 5 1 10 12 7 13 7 5 13 11 10 8 10 12 13
样例输出
5 9
思路:西墙1 :1;北墙2:10;东墙4:100;南墙8:1000.
每个方块有几面墙,二进制数位运算。
方块看作节点,相邻方块之间没有墙,则视为有边相连。房间数:所有极大连通子图数目;最大房间:所有极大连通子图中节点最多的那个
对每一个房间,进行深搜,可以得到该节点所在的极大连通子图。房间数目- -染色,用的颜色种类
#include <iostream>
using namespace std;
int R, C;//行列数
int rooms[60][60];//存储图的信息
int color[60][60];//方块是否染过色的标记 标记
int maxRoomArea = 0, roomNum = 0;//最大的面积,房间数目
int roomArea;//正在探索的连通子图面积
void Dfs(int i, int k) {
//从节点(i,k)出发,进行深度优先搜索
//即搜素该节点所在的极大连通子图
if (color[i][k])//递归结束
return;
roomArea++;//该方块加入房间,面积加1
color[i][k] = roomNum;
//对和(i,j)节点相邻的节点进行深搜
//西墙1 :1;北墙2:10;东墙4:100;南墙8:1000
if ((rooms[i][k] & 1) == 0&&k-1>=1)//向西走且不出界
Dfs(i, k - 1);
if ((rooms[i][k] & 2) == 0&&i-1>=1)//向北走且不出界
Dfs(i - 1, k);
if ((rooms[i][k] & 4) == 0&&k+1<=C)//向东走且不出界
Dfs(i, k + 1);
if ((rooms[i][k] & 8) == 0&&i+1<=R)//向南走且不出界
Dfs(i + 1, k);
}
int main() {
cin >> R >> C;
for (int i = 1; i <= R; i++)
for (int k = 1; k <= C; k++)
cin >> rooms[i][k];
memset(color, 0, sizeof(color));//所有点都标记为新点
for (int i = 1; i <= R; i++)
for (int k = 1; k <= C; k++) {//循环遍历所有的点
if (!color[i][k]) {//没有染色标记- - 在图中能找到新点
roomNum++;//找到了新的极大连通子图-新房间
roomArea = 0;
Dfs(i, k);//深搜
maxRoomArea =
max(roomArea, maxRoomArea);
}
}
cout << roomNum << endl;
cout << maxRoomArea << endl;
//复杂度 O(R*C)
}
踩方格
有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:
a. 每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;
b. 走过的格子立即塌陷无法再走第二次;
c. 只能向北、东、西三个方向走;
请问:如果允许在方格矩阵上走 n 步,共有多少种不同的方案。
2 种走法只要有一步不一样,即被认为是不同的方案。
输入格式
允许在方格上行走的步数 n(n≤20)。
输出格式
计算出的方案数量。
输出时每行末尾的多余空格,不影响答案正确性
样例输入
2
样例输出
7
思路:
递归-- - 分类讨论
从(i,j)出发,走n步的方案数,等于以下三项的和:
1)下一步向北走,走到(i-1,j),然后从(i-1,j)走n-1步的方案数;前提(i-1,j)还没有走过
2)下一步向东走,走到(i,j+1),然后从(i,j+1)走n-1步的方案数;前提(i,j+1)还没有走过
3)下一步向西走,走到(i,j-1),然后从(i,j-1)走n-1步的方案数 。前提(i,j-1)还没有走过
再分析- - 深度优先搜索
从(i,j)出发,走n步的方案数(从(i,j)出发遍历),等于以下三项的和:
1)下一步向北走,走到(i-1,j),然后从(i-1,j)走n-1步的方案数;前提(i-1,j)还没有走过- - 相邻的新点
2)下一步向东走,走到(i,j+1),然后从(i,j+1)走n-1步的方案数;前提(i,j+1)还没有走过- - 相邻的新点
3)下一步向西走,走到(i,j-1),然后从(i,j-1)走n-1步的方案数 。前提(i,j-1)还没有走过- - 相邻的新点
递归边界: n等于0时,从(i,j)出发,走0步的方案数只有一种,就是走0步
#include <iostream>
using namespace std;
int visited[100][100];
int ways(int i, int j, int n) {
//从点(i,j)出发,走n步的方案数- - 从某节点出发,遍历
if (n == 0)
return 1;
visited[i][j] = 1;//标记为旧点
int num = 0;
if (!visited[i][j - 1])//向西走
num += ways(i, j - 1, n - 1);
if (!visited[i][j + 1])//向东走
num += ways(i, j + 1, n - 1);
if (!visited[i - 1][j])//向北走
num += ways(i - 1, j, n - 1);
//以上求出了在先从(i,j)走的情况下,所有的情况
//既然已经求出了这个num了,我们就应该撤去对(i,j)的标记
//先走(m,n)的情况下,还可以再走(i,j),
//前提是,在走(m,n)之前,没有走过(i,j)
visited[i][j] = 0;//
return num;
}
int main() {
int n;
cin >> n;
//全部置为新点
memset(visited, 0, sizeof(visited));
//平面无限大,可以任选起点,
//从(25,25)出发,走不超过20步不越界- - 省去判断
cout << ways(25, 25, n) << endl;
return 0;
}
一个状态,相当于图上的一个节点,(i,j,n)节点,与状态(i,j,n)相邻的状态是(i-1,j,n-1)、(i,j-1,n-1)、(i,j+1,n-1),相邻表示有边。
在一个图上,从节点(x,y,n)出发,其中x,y任选,n是20,走到一些目标节点(x1,y1,0),求一共有多少种走法。深搜,从图上的节点出发(某个状态),走到某个目标节点或者某些目标节点(某个或者某些状态),求解一共有多少种走法,或者最近路径,或者权值最小,等等。
水洼数
题目
分析:和城堡问题相同,方向数组
#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <map>
using namespace std;
int n, m;
char field[105][105];
int visited[105][105];
int nextt[10][3] = {
{-1,-1},{-1,0},{-1,1},
{0,-1},{0,1},
{1,-1},{1,0},{1,1}
};//方向数组
void Dfs(int x, int y) {
if (visited[x][y])return;
if (!visited[x][y]) {
visited[x][y] = 1;
if (field[x][y] == '.')return;
for (int i = 0; i <= 7; i++) {
int dx = nextt[i][0], dy = nextt[i][1];
int nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < n && ny >= 0 && ny < m)
Dfs(nx, ny);
}
}
return;
}
void solve() {
int res = 0;
for(int i = 0;i <n;i++)
for(int j = 0;j<m;j++)
if (!visited[i][j] && field[i][j] == 'w') {
Dfs(i, j);
res++;
}
cout << res << endl;
}
void init() {
cin >> n >> m;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
cin >> field[i][j];
memset(visited, 0, sizeof(visited));
}
int main() {
init();
solve();
return 0;
}
n后问题
N后问题就是在N*N格的棋盘上面放置彼此不受攻击的n个皇后。这里的皇后指的是国际象棋中的皇后,按照国际象棋的规则,皇后可以攻击当前行和当前列还有同一斜线的棋子。简单来说,就是n个皇后的位置不可以在同一行,同一列,同一斜线。
思路:n重for循环改为递归,每次递归解决一层for循环的任务
下面来了,每层for循环枚举的是什么,先以三皇后问题为例
int counts = 0;
for(int i = 1;i <=3;i++)
for(int j = 1;j <=3;j++)
for(int k = 1;k <= 3;k++)
{
//i、j、k分别表示第一行、第二行、第三行皇后的摆放位置
//满足的条件是:三个皇后不再同一列、不在同意行、不在对角线位置
if(满足条件)
counts++;
}
事实上,n皇后问题就是类似的n重循环,由于n是变化的,所以以递归来实现,递归函数的定义
dfs(int num),当前需要摆放第num行的皇后(行数从1开始,摆完第n行结束),前1到num-1行的皇后都摆放好了(摆放的信息存储在哪里呢,因为我们要判断第num行的皇后摆在哪里是合理的,所以需要前面的信息),dfs(num)表示前1到num-1行都已摆好,现在从num行摆,直到摆到到第n行,终止条件是num == n+1,终止时需要做什么呢
void Nqueen(int k )//从第k个皇后还是向后摆,假设前面0-k-1都已经摆好了
{
int I;
if(k == N){//k是N,说明已经全部摆好了
for(i=0;i<N;i++)
cout<<queenPos[i]+1;
cout<<endl;//每组一换行
return ;
}
for(i=0;i<N;i++){//开始找第k个皇后的位置- - 相当于一重循环 - -具体的八皇后是8重循环
int j;
for(j = 0;i< k;j++){//冲突不冲突- - 太复杂的话,可以写个函数,直接在这里调用
if(queenPos[j] == i ||abs(queenPos[j]-i)==abs(k-j)){
break;//冲突,跳出循环
}
}
if(j == k)//上面的没有跳出循环,没有冲突
{
queenPos[k] = I;//不冲突,我们摆放第k个皇后在位置I
Nqueen(k+1);//前0-k个已经排好了,排第k+1及其后面的}
}
王后问题
问题描述
地球人都知道,在国际象棋中,后如同太阳,光芒四射,威风八面,它能控制横、坚、斜线位置。
看过清宫戏的中国人都知道,后宫乃步步惊心的险恶之地。各皇后都有自己的势力范围,但也总能找到相安无事的办法。
所有中国人都知道,皇权神圣,伴君如伴虎,触龙颜者死......
现在有一个n*n的皇宫,国王占据他所在位置及周围的共9个格子,这些格子皇后不能使用(如果国王在王宫的边上,占用的格子可能不到9个)。当然,皇后也不会攻击国王。
现在知道了国王的位置(x,y)(国王位于第x行第y列,x,y的起始行和列为1),请问,有多少种方案放置n个皇后,使她们不能互相攻击。
数据规模和约定
n<=12
【输入形式】
一行,三个整数,皇宫的规模及表示国王的位置
【输出形式】
一个整数,表示放置n个皇后的方案数
【样例输入】
8 2 2
【样例输出】
10
如何解决处于对角线位置时,国王的遮挡
题目只是说,国王占据了那几个位置,那几个位置我们不能放棋子,并不是说国王真的摆放在那个位置(x,y)了,所以这个遮挡问题不要考虑了。- - 考虑的话也不好写。。。
#include<iostream>
#include<cmath>
using namespace std;
int n, x, y;
int ans = 0;
int NQueenPos[100];//存储摆放的皇后位置
//已知(i,NQueenPos[i]),i = 1,2,...,k-1,求(k,NQueenPos[k])
void f(int k);//在摆放了第1~k-1行的皇后之后,从第k行开始摆皇后,直到摆到第n行,
//就是一行一行摆皇后,已经摆了k-1个皇后了,再摆剩下的
int main() {
cin >> n >> x >> y;
f(1);
cout << ans;
return 0;
}
void f(int k) {
if (k == n + 1) {
ans++;
return;
}
for (int i = 1; i <= n; i++) {//尝试(k,NQueenPos[k]),NQueenPos[k] = i
int j;
for (j = 1; j < k; j++) {//已经摆好的
if(i == NQueenPos[j]||abs(k-j) == abs(i-NQueenPos[j]))
{
break;
}
}
if (j == k) {
if (!(k <= x + 1 && k >= x - 1 && i >= y - 1 && i <= y + 1))
{
NQueenPos[k] = i;
f(k + 1);
}
}
}
}
上面是使用直接去做,也可以使用标记数组,省去
for (j = 1; j < k; j++) {//已经摆好的
if(i == NQueenPos[j]||abs(k-j) == abs(i-NQueenPos[j]))
{
break;
}
}
将这段简化为for (j = 1; j < k; j++) {//已经摆好的
if(abs(k-j) == abs(i-NQueenPos[j]))
{
break;
}
}
#include<iostream>
#include <cmath>
using namespace std;
int n, x, y;
int ans = 0;
int NQueenPos[100];//存储摆放的皇后位置
//采用标记数组进行判断
int vis[100];//标记vis[i] 标记NQueen[i]是否被用过,控制列
//已知(i,NQueenPos[i]),i = 1,2,...,k-1,求(k,NQueenPos[k])
void f(int k);//在摆放了第1~k-1行的皇后之后,从第k行开始摆皇后,直到摆到第n行,
//就是一行一行摆皇后,已经摆了k-1个皇后了,再摆剩下的
int main() {
cin >> n >> x >> y;
f(1);
cout << ans;
return 0;
}
void f(int k) {
//本轮递归,解决的是(k,NQueenPos[k])中NQueenPos[k]的值,来解决第k行的摆放
if (k == n + 1) {
ans++;
return;
}
for (int i = 1; i <= n; i++) {//尝试(k,NQueenPos[k]),NQueenPos[k] = i
int j;
if (!vis[i] && !(k <= x + 1 && k >= x - 1 && i >= y - 1 && i <= y + 1))
{//列可以且不在王的位置
for (j = 1; j < k; j++) {//已经摆好的
if (abs(k - j) == abs(i - NQueenPos[j]))
{
break;
}
}
if (j == k) {
NQueenPos[k] = i;
vis[i] = 1;//用了NQueenPos[k]表示的列,这一列不能再放棋子了
f(k + 1);//摆好剩下的皇后
vis[i] = 0;//恢复现场,
//在第k行第NQueenPos[k]列摆皇后的所有方案已经列举出来了,撤销标记
}
}
}
}
2n皇后问题
给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。
输入
输入的第一行为一个整数n,表示棋盘的大小。
接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。
输出
输出一个整数,表示总共有多少种放法。
样例输入
4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
样例输出
2
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
using namespace std;
#include <iostream>
#include<math.h>
using namespace std;
int n;
int map[9][9];
int bq[9], wq[9];
//定义bq,wq分别用于存储 在每一行都在哪一个位置摆放了黑(black queen),白皇后 (write queen)
int sum = 0;
bool Ispos(int cur, int* q)//判断皇后是否满足条件
{
for (int i = 1; i < cur; i++)
{
if (q[i] == q[cur] || (abs(i - cur) == abs(q[i] - q[cur])))
return false;
//若有一个满足上面的条件的,就会返回false
}
return true;
}
void WQ(int cur)
{
if (cur == n + 1)//白皇后也全部放置,次数+1
{
sum++;
}
for (int i = 1; i <= n; i++)
{
if (bq[cur] == i)continue;//判断是否黑皇后已摆放的位置
if (map[cur][i] == 0)continue;
wq[cur] = i;
if (Ispos(cur, wq))
{
WQ(cur + 1);
}
}
}
void BQ(int cur)
{
if (cur == n + 1) //若黑皇后放置完,再处理白皇后
{
WQ(1);
}
for (int i = 1; i <= n; i++)
{
if (map[cur][i] == 0)continue;//若map对应的位置为0,即不能放置皇后 ,跳过这次循环
bq[cur] = i;
if (Ispos(cur, bq))
{
BQ(cur + 1);
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)// i = 1为了与一般认识一样
{
for (int j = 1; j <= n; j++)// j = 1为了与一般认识一样
{
cin >> map[i][j];
}
}
BQ(1);//执行摆放黑皇后函数(当然先摆放白皇后也行,不过要改一些),
//里面是1,表示从第1列开始,放置
cout << sum;
return 0;
}
盾神与困难数独
问题描述
有一天,盾神接触到了风靡世界的小游戏——数独!!!盾神非常感兴趣,不惜翘课用了一天的时间把数独玩得出神入化!!!于是他要过来考考你。经过“盾神与简单数独”的磨练后,你会做9*9的了。
输入格式
输入为9*9的矩阵,如果第i行第j列为0,则该格子未填数;否则该格子已经有数。
输出格式
输出为1个9*9的矩阵,表示字典序最小的方案。如无解则输出NO。
矩阵大小关系的定义:第一关键字为a[1][1],第二关键字为a[1][2],……第四关键字为a[1][4],第五关键字为a[2][1],以此类推。矩阵A小于矩阵B,当且仅当存在k,A和B的前k-1个关键字的值都相等,而A的第k个关键字的值小于B的第k个关键字的值。矩阵A等于矩阵B,当且仅当A小于B和B小于A都不成立。
字典序升序的定义:在矩阵序列a中,对于任意的i<=j,有a[i]<=a[j]。
样例输入
1 2 3 4 5 6 7 8 9
2 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0
7 0 0 0 0 0 0 0 0
8 0 0 0 0 0 0 0 0
9 0 0 0 0 0 0 0 0
样例输出
NO
样例输入
1 2 3 4 5 6 7 8 9
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
样例输出
1 2 3 4 5 6 7 8 9
4 5 6 7 8 9 1 2 3
7 8 9 1 2 3 4 5 6
2 1 4 3 6 5 8 9 7
3 6 5 8 9 7 2 1 4
8 9 7 2 1 4 3 6 5
5 3 1 6 4 2 9 7 8
6 4 2 9 7 8 5 3 1
9 7 8 5 3 1 6 4 2
数据规模和约定
矩阵中所有数的值为0到9。
补充一个常识:数独定义
数独(shù dú)是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复 。
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
大致思路
每个3*3的粗线宫都含数字1-9,dfs,判断每行每列是否也满足1-9
上面的思路是错误的,不需要对每一个3*3的粗线格进行dfs,只要把3*3的粗线格的看作和行列一样的地位即可,就是判重
代码如下
//#include<bit/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <cstring>//memset
#include <string>
using namespace std;
//const int maxn = 1e8;
bool IsPrime(int m) {
if (m == 2)
return true;
int tmp = sqrt(m);
for (int i = 2; i <= tmp; i++)
if (m % i == 0)
return false;
return true;
}
int gcd_wx(int a, int b) {
while (a != b)
{
if (a > b)
a = a - b;
else
b = b - a;
}
return a;
}
bool leapYear(int y) {
if (y % 400 == 0)
return true;
if (y % 4 == 0 && y % 100 != 0)
return true;
return false;
}
int dx[4] = { 1,0,0,-1 };
int dy[4] = { 0,-1,1,0 };
char dire[4] = { 'D','L','R','U' };
struct Step {
int x;
int steps;//第steps步走到位置x
Step(int xx, int s) :x(xx), steps(s) {
}
};
//queue<Step>q;
struct node {
//输出最短路径的步数和路径
int x;
int y;
int front;//记录上一个节点
int steps;
char dir;
node() :x(-1), y(-1), front(-1), steps(0), dir('w') {
}
node(int xx, int yy, int ff, int s, char d) :x(xx), y(yy), front(ff), steps(s), dir(d) {
}
};
struct dish {
string s;
int steps;
int front;
int dir;
dish() :s("aaaaaaaaaa"), steps(-1), front(-91), dir(-1) {
}//定义这个用于vector<dish> dque(1000000)
dish(string ss, int s, int ff, int d) :s(ss), steps(s), front(ff), dir(d) {
}//定义这个用于赋值构造
};
struct fat {
int x;
int y;
int steps;
fat() :x(-1), y(-1), steps(-1) {
}
fat(int xx, int yy, int s) :x(xx), y(yy), steps(s) {
}
};
int mp[10][10];//存储地图
//理解标记数组定义
bool colFlag[10][10];//列标记,colFlag[i][j]=1表示第i列中已经有j这个数了
bool rowFlag[10][10];//行标记,rowFlag[i][j] = 1表示第i行中已经有j这个数了
bool flag33[10][10];//3_by_3方阵的标记,flag33[i][j] = 1表示第i个3*3的方阵中有j这个数了,方阵一共有9个,编号从左至右,从上到下依次是0-1-2...-8
int dfs(int num){
//在进行dfs之前,我们对于已经出现的数进行了三种标记-行标记列标记和group33标记
//从第num个数开始,排到最后一个数,得到的方阵符合数独则返回1,否则返回0
if(num == 81){
for(int i = 0;i < 9;i++){
for(int j = 0;j <9;j++)
cout<<mp[i][j]<<" ";
cout<<endl;
}
return 1;
}
else{
//怎么获得下面这三个数
//获得第num个数所在的行号、列号、和小方阵号
int row = num/9;
int col = num%9;
int groupIndex = (row/3)*3+col/3;
if(mp[row][col] == 0){//还没有填数
for(int i = 1;i <= 9;i++){//填1-9 ,按照从1到9进行遍历,可以获得字典序最小
if(colFlag[row][i]||rowFlag[col][i]||flag33[groupIndex][i])continue;//重复了直接跳过
//打标记
mp[row][col] = i;
colFlag[row][i] = 1;
rowFlag[col][i] = 1;
flag33[groupIndex][i] = 1;
//下一次搜索
if(dfs(num+1))
return 1;//这和以前不一样?原因:这是在找路,找第一条路 ,找到了就立刻返回
//去标记-恢复现场
mp[row][col] = 0;
colFlag[row][i] = 0;
rowFlag[col][i] = 0;
flag33[groupIndex][i] = 0;
}
}
else{
if(dfs(num+1))
return 1;
}
return 0;
}
}
int main() {
//接受输入数据
for(int i = 0;i < 9;i++)
for(int j = 0;j < 9;j++)
{
cin>>mp[i][j];
int groupIndex = (i/3)*3+j/3;
if(mp[i][j]) {
if(colFlag[i][mp[i][j]]||rowFlag[j][mp[i][j]]||flag33[groupIndex][mp[i][j]]){
cout<<"NO";
return 0;
}
colFlag[i][mp[i][j]] = 1;
rowFlag[j][mp[i][j]] = 1;
flag33[groupIndex][mp[i][j]] = 1;
}
}
int ans = dfs(0);
if(ans == 0)cout<<"NO";
return 0;
}
dfs进行字典序最小情况搜索
分析上面的代码,这实际上是利用dfs来找一条路径----之前都是利用bfs进行最小字典序路径的搜索。根据上面的代码,可以看出,利用dfs进行字典序最小情况搜索时,具体操作如下, 此时,dfs时,我们设置返回值来表示当前是否找到了路径,找到了即返回,
//下一次搜索
if(dfs(num+1))
return 1;//这和以前不一样?
//原因:这是在找路,找第一条路 ,找到了就立刻返回
一般情况下,这里的话是dfs(num+1));在这里,这里找到了一条路就返回,不进行深搜,停止。
此外,这里,由num转化为row和col和groupIndex,这是较为常规的,这个思路可以借鉴二维数组转化为一维数组,int a[3][4],那么a[i][j]就是数组a中的第4*(i-1)+j -1个数。 同理,对于数组中第m个数,对应a[m/4][m%4]。
下面再给一例:
剪邮票
如下图, 有12张连在一起的12生肖的邮票。现在你要从中剪下5张来,要求必须是连着的。(仅仅连接一个角不算相连)
比如,下面两张图中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
分析:这道题中,我们每次从12个数中找5个数,利用二进制状态压缩(1表示选了,0表示没选),不过,需要将这个状态转化为这个数组里的数是否被选到了。
下面的代码是错误的
//#include<bit/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <cstring>//memset
#include <string>
using namespace std;
//const int maxn = 1e8;
bool IsPrime(int m) {
if (m == 2)
return true;
int tmp = sqrt(m);
for (int i = 2; i <= tmp; i++)
if (m % i == 0)
return false;
return true;
}
int gcd_wx(int a, int b) {
while (a != b)
{
if (a > b)
a = a - b;
else
b = b - a;
}
return a;
}
bool leapYear(int y) {
if (y % 400 == 0)
return true;
if (y % 4 == 0 && y % 100 != 0)
return true;
return false;
}
int dx[4] = { 1,0,0,-1 };
int dy[4] = { 0,-1,1,0 };
char dire[4] = { 'D','L','R','U' };
struct Step {
int x;
int steps;//第steps步走到位置x
Step(int xx, int s) :x(xx), steps(s) {
}
};
//queue<Step>q;
struct node {
//输出最短路径的步数和路径
int x;
int y;
int front;//记录上一个节点
int steps;
char dir;
node() :x(-1), y(-1), front(-1), steps(0), dir('w') {
}
node(int xx, int yy, int ff, int s, char d) :x(xx), y(yy), front(ff), steps(s), dir(d) {
}
};
struct dish {
string s;
int steps;
int front;
int dir;
dish() :s("aaaaaaaaaa"), steps(-1), front(-91), dir(-1) {
}//定义这个用于vector<dish> dque(1000000)
dish(string ss, int s, int ff, int d) :s(ss), steps(s), front(ff), dir(d) {
}//定义这个用于赋值构造
};
struct fat {
int x;
int y;
int steps;
fat() :x(-1), y(-1), steps(-1) {
}
fat(int xx, int yy, int s) :x(xx), y(yy), steps(s) {
}
};
int mp[10][10];
bool visited[10][10];
int getiBit(int n,int i) {
return (n>>i)&1;
}
bool checkFirst(int state){
int count = 0;
for(int i = 0;i < 12;i++)
if(getiBit(state,i))
count++;
return count==5;
}
void dfs(int x,int y){
if(visited[x][y]||!mp[x][y])return;
visited[x][y] = true;//标记
for(int i = 0;i < 4;i++){
int ax = x+dx[i];
int ay = y+dy[i];
if(ax<0&&ax>3&&ay<0&&ay>2&&!visited[ax][ay]&&mp[ax][ay])
dfs(ax,ay);
}
return;
}
int main() {
int res = 0;
for(int i = 1;i <(1<<12);i++)
if(checkFirst(i)){
memset(visited,0,sizeof(visited));
for(int j = 0;j < 12;j++)
mp[j/4][j%4] = getiBit(i,j);
int flag = 0;
for(int i1 = 0;i1 < 4;i1++){
for(int j1 = 0;j1 < 3;j1++)
{
if(mp[i1][j1]){
dfs(i1,j1);
flag = 1;
break;
}
}
if(flag == 1){
int ans = 0;
for(int i2 = 0;i2 < 4;i2++)
for(int j2 = 0;j2 < 3;j2++)
{
if(visited[i2][j2])ans++;
}
if(ans==5) res++;
break;
}
}
}
cout<<res<<endl;
return 0;
}
正确的代码如下,结果是116
//#include<bit/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <cstring>//memset
#include <string>
using namespace std;
//const int maxn = 1e8;
bool IsPrime(int m) {
if (m == 2)
return true;
int tmp = sqrt(m);
for (int i = 2; i <= tmp; i++)
if (m % i == 0)
return false;
return true;
}
int gcd_wx(int a, int b) {
while (a != b)
{
if (a > b)
a = a - b;
else
b = b - a;
}
return a;
}
bool leapYear(int y) {
if (y % 400 == 0)
return true;
if (y % 4 == 0 && y % 100 != 0)
return true;
return false;
}
int dx[4] = { 1,0,0,-1 };
int dy[4] = { 0,-1,1,0 };
char dire[4] = { 'D','L','R','U' };
struct Step {
int x;
int steps;//第steps步走到位置x
Step(int xx, int s) :x(xx), steps(s) {
}
};
//queue<Step>q;
struct node {
//输出最短路径的步数和路径
int x;
int y;
int front;//记录上一个节点
int steps;
char dir;
node() :x(-1), y(-1), front(-1), steps(0), dir('w') {
}
node(int xx, int yy, int ff, int s, char d) :x(xx), y(yy), front(ff), steps(s), dir(d) {
}
};
struct dish {
string s;
int steps;
int front;
int dir;
dish() :s("aaaaaaaaaa"), steps(-1), front(-91), dir(-1) {
}//定义这个用于vector<dish> dque(1000000)
dish(string ss, int s, int ff, int d) :s(ss), steps(s), front(ff), dir(d) {
}//定义这个用于赋值构造
};
struct fat {
int x;
int y;
int steps;
fat() :x(-1), y(-1), steps(-1) {
}
fat(int xx, int yy, int s) :x(xx), y(yy), steps(s) {
}
};
int mp[10][10];
int visited[10][10];
int getiBit(int n, int i) {
return (n >> i) & 1;
}
bool checkFirst(int state) {
int count = 0;
for (int i = 0; i < 12; i++)
if (getiBit(state, i))
count++;
return count == 5;
}
void dfs(int x, int y) {
if (visited[x][y])return;
visited[x][y] = 1;//标记
for (int i = 0; i < 4; i++) {
int ax = x + dx[i];
int ay = y + dy[i];
if (ax >= 0 && ax<=2 && ay >= 0 && ay<=3 && !visited[ax][ay] && mp[ax][ay])
dfs(ax, ay);
}
return;
}
int main() {
int res = 0;
for (int i = 1; i < (1 << 12); i++)
if (checkFirst(i)) {
memset(visited, 0, sizeof(visited));
for (int j = 0; j < 12; j++)
mp[j / 4][j % 4] = getiBit(i, j);
for (int i2 = 0; i2 < 3; i2++) {
for (int j2 = 0; j2 < 4; j2++)
{
cout << mp[i2][j2] << " ";
}
cout << endl;
}
int flag = 0;
for (int i1 = 0; i1 < 3; i1++) {
for (int j1 = 0; j1 < 4; j1++)
{
if (mp[i1][j1]) {
dfs(i1, j1);
flag = 1;
break;
}
}
if (flag == 1) {
int ans = 0;
for (int i2 = 0; i2 < 3; i2++)
{
for (int j2 = 0; j2 < 4; j2++)
{
cout << visited[i2][j2] << " ";
if (visited[i2][j2])ans++;
}
cout << endl;
}
if (ans == 5) res++;
break;
}
}
}
cout << res << endl;
return 0;
}
正确代码或者如下
//#include<bit/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <cstring>//memset
#include <string>
using namespace std;
//const int maxn = 1e8;
bool IsPrime(int m) {
if (m == 2)
return true;
int tmp = sqrt(m);
for (int i = 2; i <= tmp; i++)
if (m % i == 0)
return false;
return true;
}
int gcd_wx(int a, int b) {
while (a != b)
{
if (a > b)
a = a - b;
else
b = b - a;
}
return a;
}
bool leapYear(int y) {
if (y % 400 == 0)
return true;
if (y % 4 == 0 && y % 100 != 0)
return true;
return false;
}
int dx[4] = { 1,0,0,-1 };
int dy[4] = { 0,-1,1,0 };
char dire[4] = { 'D','L','R','U' };
struct Step {
int x;
int steps;//第steps步走到位置x
Step(int xx, int s) :x(xx), steps(s) {
}
};
//queue<Step>q;
struct node {
//输出最短路径的步数和路径
int x;
int y;
int front;//记录上一个节点
int steps;
char dir;
node() :x(-1), y(-1), front(-1), steps(0), dir('w') {
}
node(int xx, int yy, int ff, int s, char d) :x(xx), y(yy), front(ff), steps(s), dir(d) {
}
};
struct dish {
string s;
int steps;
int front;
int dir;
dish() :s("aaaaaaaaaa"), steps(-1), front(-91), dir(-1) {
}//定义这个用于vector<dish> dque(1000000)
dish(string ss, int s, int ff, int d) :s(ss), steps(s), front(ff), dir(d) {
}//定义这个用于赋值构造
};
struct fat {
int x;
int y;
int steps;
fat() :x(-1), y(-1), steps(-1) {
}
fat(int xx, int yy, int s) :x(xx), y(yy), steps(s) {
}
};
int mp[10][10];
bool visited[10][10];
int getiBit(int n,int i) {
return (n>>i)&1;
}
bool checkFirst(int state){
int count = 0;
for(int i = 0;i < 12;i++)
if(getiBit(state,i))
count++;
return count==5;
}
void dfs(int x,int y){
if(visited[x][y]||!mp[x][y])return;
visited[x][y] = true;//标记
for(int i = 0;i < 4;i++){
int ax = x+dx[i];
int ay = y+dy[i];
if(ax<0&&ax>3&&ay<0&&ay>2&&!visited[ax][ay]&&mp[ax][ay])continue;
dfs(ax,ay);
}
return;
}
int main() {
int res = 0;
for(int i = 1;i <(1<<12);i++)
if(checkFirst(i)){
memset(visited,0,sizeof(visited));
for(int j = 0;j < 12;j++)
mp[j/4][j%4] = getiBit(i,j);
int flag = 0;
for(int i1 = 0;i1 < 3;i1++){
for(int j1 = 0;j1 < 4;j1++)
{
if(mp[i1][j1]){
dfs(i1,j1);
flag = 1;
break;
}
}
if(flag == 1){
int ans = 0;
for(int i2 = 0;i2 < 3;i2++)
for(int j2 = 0;j2 < 4;j2++)
{
if(visited[i2][j2])ans++;
}
if(ans==5) res++;
break;
}
}
}
cout<<res<<endl;
return 0;
}