n皇后--王、后传说--递归--2n皇后

 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值