一、剪邮票问题
题目描述![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/0f5553b46e4b93c5b7d50f6e196664ad.png)
解题思路
把12选5的各种选法排列出来,并判断是不是5格连着。
判断方法:把所有的红格子设为1,其它设置为0,在红格子中随便选一个(后面代码具体实现,用的都是最后一个红格子,这无所谓),从这个格子出发,按照它的周围4个方向尝试一遍,一个格子被走过立即设置为0,看走完后是不是走了5个。
代码
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int a[5][5]; //二维数组存储方格图
//dx与dy配合使用进行方格间的移动操作
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
int dfs(int x, int y)//参数为该点的坐标
{
int ans = 1;
a[x][y] = 0; //走过该点置为0
for(int i = 0; i < 4; i++){ //分别走到上、右、下、左点
int nextx = x + dx[i]; //下一个点的横坐标
int nexty = y + dy[i]; //下一个点的纵坐标
if(nextx >= 0 && next < 3 && nexty >= 0 && nexty < 4 && a[nextx][nexty]){ //判断下标是否越界并是否为1
ans += dfs(nextx, nexty);
}
}
return ans;
}
int main()
{
int ans = 0;
string s = "000000011111"; //用来表示每个方格的状态 0 或 1
do{
int start; //出发点
//一维转二维并设最后一点为出发点
for(int i = 0; i < s.length(); i++){
a[i/4][i%4] = (s[i] == '1' ? 1 : 0);
if('1' == s[i])
start = i;
}
//以出发点做起始进行深搜
if(dfs(start / 3, start % 4) == 5)
ans++;
}while(next_permutation(s,begin(), s.end()));
cout << ans << endl;
}
二、方格对称问题
题目描述
解题思路
根据题目要求,我们从中心位置开始,向边界做深搜。为了保证对称,在dfs过程中,每一步都标记两个位置,当前位置和关于中心对称的位置,直到走到6*6大方格的最外侧,记录多少种走法。由于旋转对称的需要,最终答案是关于上述走法除以4.
代码
#include <iostream>
int N = 6;
int a[N][N];
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
int ans;
void dfs(int x, int y)
{
//到达边界则使结果加一并返回
if(x == 0 || x == N || y == 0 || y == N){
ans++;
return;
}
for(int i = 0; i < 4; i++){
int nextx = x + dx[i];
int nexty = y + dy[i];
if(a[nextx][nexty]) //如果下一点点已经被遍历过,则continue
continue;
a[nextx][nexty] = 1; //下一点设为1
a[N - nextx][N - nexty] = 1; //关于下一点对称的点设为1
dfs(nextx, nexty);
//恢复操作便于下一轮遍历
a[nextx][nexty] = 0;
a[N - nextx][N - nexty] = 0;
}
return;
}
int main()
{
v[N / 2][N / 2] = 1;//设置出发点为1
dfs(N / 2, N / 2);
cout << ans / 4 << endl;
return 0;
}
3.岛屿沉没(无向图的计数)
问题描述
你有一张某海域NxN像素的照片,".“表示海洋、”#"表示陆地,如下所示:
…
.##…
.##…
…##.
…####.
…###.
…
其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
…
…
…
…
…#…
…
…
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
【输入格式】
第一行包含一个整数N。 (1 <= N <= 1000)
以下N行N列代表一张海域照片。
照片保证第1行、第1列、第N行、第N列的像素都是海洋。
【输出格式】
一个整数表示答案。
【输入样例】
7
…
.##…
.##…
…##.
…####.
…###.
…
【输出样例】
1
解题思路
图的深搜,在每次深搜时每个节点只需访问一次,所以在深搜过程中不用返回状态
1.原岛屿的计数
以每个未访问过的#为起点,对相连的#进行深度搜索,同时改变该点的访问状态(visit [x][y] 设为true),结束后使cnt++,cnt即为沉没前岛屿的个数
2.岛屿沉没操作
遍历每块#,若#的上下左右都为#,则该点不沉没,使状态值 设为false。
遍历结束后,每块沉没的陆地状态值为true,在下面的深搜中不再被访问。
3.岛屿沉没的计数
计算出沉没后的岛屿数量(同原岛屿的计数),然后相减
代码
#include <iostream>
#include <vector>
using namespace std;
vector< vector<char> > a;
vector< vector<bool> >visit;
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
void dfs(int x, int y)
{
//如果该点为海洋或该点已经访问过返回
if(a[x][y] == '.' || b[x][y] == true)
return;
b[x][x] = true;
for(int k = 0; k < 4; k++){
int nextx = x + dx[k];
int nexty = y + dy[k];
dfs(nextx, nexty);
}
}
int main()
{
int num;
int cnt1 = 0, cnt2 = 0;
cin >> num;
a.resize(num, vector<char>(num));//二维vector的重构
/*vector<vector<int> > v(r, vector<int>(c, 0));
v.resize(n);
for(int i = 0; i < n; i++)
v[i].resize(n);
*/
for(int i = 0; i < num; i++){
for(int j = 0; j < num; j++){
char c;
cin >> c;
a.push_back(c);
}
}
/*错误的初始化一个int char[7][7]方式
a = {{'.','.','.','.','.','.','.'},
{'.','#','#','.','.','.','.'},
{'.','#','#','.','.','.','.'},
{'.','.','.','.','#','#','.'},
{'.','.','#','#','#','#','.'},
{'.','.','.','#','#','#','.'},
{'.','.','.','.','.','.','.'}};
正确的初始化一个int char[8][8]方式
a = {"1234567",
"2234567",
"3234567",
"4234567",
"5234567",
"6234567",
"7234567",} */
for(int i = 0; i < num; i++){
for(int j = 0; j < num; j++){
if(a[i][j] == '#' && b[i][j] == false)
{
cnt1++;
dfs(i, j);
}
}
}
for(int i = 0; i < num; i++){
for(int j = 0; j < num; j++){
if(a[i][j] == '#'){
if(a[i - 1][j] == '#' && a[i][j + 1] == '#' && a[i + 1][j] == '#' && a[i][j - 1] == '#')
b[i][j] = false;
}
}
}
for(int i = 0; i < 7; i++){
for(int j = 0; j < 7; j++){
if(a[i][j] == '#' && b[i][j] == false){
cnt2++;
dfs(i, j);
}
}
}
cout << cnt1 - cnt2 << endl;
}