从起点出发,走过的点要做标记,发现有没走过的点,就随意挑一个往前走,走不 了就回退,此种路径搜索策略就称为“深度优先搜索”,简称“深搜”。
(1)判断从V出发是否能走到终点:
bool Dfs(V) {
if( V 为终点)
return true;
if( V 为旧点)
return false;
将V标记为旧点;
对和V相邻的每个节点U {
if( Dfs(U) == true)
return true;
}
return false;
}
int main() {
将所有点都标记为新点;
起点 = 1
终点 = 8
cout << Dfs(起点);
}
(2)判断从V出发是否能走到终点,如果能,要记录路径:
Node path[MAX_LEN]; //MAX_LEN取节点总数即可
int depth;
bool Dfs(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;
}
}
题目描述
上图是一个城堡地形图,请你编写一个程序,计算城堡一共有多少个房间,最大的房间有多大。城堡被分割成m*n(m<=50,n<=50)个方块,每个方块可以有0~4面墙。
输入:
输出:
样例输入:
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 2 3 3 3
1 1 1 2 3 4 3
1 1 1 5 3 5 3
1 5 5 5 5 5 3
从而一共有5个房间,最大的房间(1)占据9个格子。
解答
#include
#include
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) {
if (color[i][k]) {
return;
}
++roomArea;
color[i][k] = roomNum;
if ((rooms[i][k] & 1) == 0) Dfs(i, k-1); // 向西走
if ((rooms[i][k] & 2) == 0) Dfs(i-1, k); // 向北走
if ((rooms[i][k] & 4) == 0) Dfs(i, k+1); // 向东走
if ((rooms[i][k] & 8) == 0) Dfs(i+1, k); // 向南走
}
int main() {
cin >> R >> C;
for (int i = 1; i <= R; i++)
for (int j = 1; j <= C; j++)
cin >> rooms[i][j];
memset(color, 0, sizeof(color));
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
if (!color[i][j]) {
++roomNum;
roomArea = 0;
Dfs(i, j);
maxRoomArea = max(maxRoomArea, roomArea);
}
}
}
cout << roomNum << endl;
cout << maxRoomArea << endl;
return 0;
}
题目描述
有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:
a. 每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;
b. 走过的格子立即塌陷无法再走第二次;
c. 只能向北、东、西三个方向走;
请问:如果允许在方格矩阵上走n步(n<=20),共有多少种不同的方案。 2种走法只要有一步不一样,即被认为是不同的方案。
解题思路
递归
从 (i,j) 出发,走n步的方案数,等于以下三项之和:
解答
#include
#include
using namespace std;
int visited[30][50];
int ways(int i, int j, int 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);
visited[i][j] = 0;
return num;
}
int main() {
int n;
cin >> n;
memset(visited, 0, sizeof(visited));
cout << ways(0, 25, n) << endl;
return 0;
}
题目描述
N个城市,编号1到N。城市间有R条单向道路。每条道路连接两个城市,有长度和过路费两个属性。Bob只有K块钱,他想从城市1走到城市N。问最短共需要走多长的路。如果到不了N ,输出-1。
2<=N<=100
0<=K<=10000
1<=R<=10000
每条路的长度 L, 1 <= L <= 100
每条路的过路费T , 0 <= T <= 100
解题思路
从城市 1开始深度优先遍历整个图,找到所有能过到达 N 的走法,选一个最优的。
最优性剪枝:
1) 如果当前已经找到的最优路径长度为L ,那么在继续搜索的过程中,总长度已经大于 等于L的走法,就可以直接放弃,不用走到底了。
另一种通用的最优性剪枝思想 —保存中间计算结果用于最优性剪枝:
2) 用midL[k][m] 表示:走到城市k时总过路费为m的条件下,最优路径的长度。若在 后续的搜索中,再次走到k时,如果总路费恰好为m,且此时的路径长度已经超过 midL[k][m],则不必再走下去了。
解答
#include
#include
#include
using namespace std;
int K, N, R;
struct Road{
int d, L, t;
};
vector<vector > cityMap(110); // 邻接表,cityMap[i]是从点i有路连到的城市集合
int minLen = 1 << 30; // 当前找到的最优路径的长度
int totalLen; // 正在走的路径的长度
int totalCost; // 正在走的路径的花销
int visited[110]; // 城市是否已经走过的标记
int minL[110][10100]; // minL[i][j]表示从1到i点的,花销为j的最短路的长度
void Dfs(int s) { // 从s点开始向N走
if(s == N) {
minLen = min(minLen, totalLen);
return;
}
for (int i = 0; i < cityMap[s].size(); i++) {
int d = cityMap[s][i].d; // s有路连到d
if (!visited[d]) {
int cost = totalCost + cityMap[s][i].t;
if (cost > K)
continue;
if (totalLen + cityMap[s][i].L >= minLen ||
totalLen + cityMap[s][i].L >= minL[d][cost])
continue;
totalLen += cityMap[s][i].L;
totalCost += cityMap[s][i].t;
minL[d][cost] = totalLen;
visited[d] = 1;
Dfs(d);
visited[d] = 0;
totalCost -= cityMap[s][i].t;
totalLen -= cityMap[s][i].L;
}
}
}
int main() {
cin >> K >> N >> R;
for (int i = 0; i < R; i++) {
int s;
Road r;
cin >> s >> r.d >> r.L >> r.t;
if (s != r.d)
cityMap[s].push_back(r);
}
for (int i = 0; i < 110; i++)
for (int j = 0; j < 10010; j++)
minL[i][j] = 1 << 30;
memset(visited, 0, sizeof(visited));
totalLen = 0;
totalCost = 0;
visited[1] = 1;
minLen = 1 << 30;
Dfs(1);
if (minLen < (1 << 30))
cout << minLen << endl;
else
cout << "-1" << endl;
return 0;
}
题目描述
要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。 设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时 ,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的 下底面除外)的面积Q最小。
令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小 。 (除Q外,以上所有数据皆为正整数)
解题思路
解答
#include
#include
#include
#include
using namespace std;
int N, M;
int minArea = 1 << 30; // 最优表面积
int area = 0; // 正在搭建中的蛋糕的表面积
void Dfs(int v, int n, int r, int h) {
// 要用n层去凑体积v, 最底层半径不能超过r,高度不能超过s
// 求出最小表面积放入minArea
if (n == 0) {
if (v) // 没有层数了,但是还剩体积未搭
return;
else
minArea = min(minArea, area);
}
if (v <= 0)
return;
for (int rr = r; rr >= n; rr--) { // 半径和高要足够被减小
if (n == M) // 底面积
area = rr * rr;
for (int hh = h; hh >= n; hh--) {
area += 2 * rr * hh;
Dfs(v-rr*rr*hh, n-1, rr-1, hh-1);
area -= 2 * rr * hh;
}
}
}
int main() {
// Dfs(N, M, maxR, maxH);
if (minArea == (1 << 30))
cout << 0 << endl;
else
cout << minArea << endl;
return 0;
}