包含洛谷和vjudgekuangbin题目(慢慢更新中)
洛谷
1.马的遍历
题目链接:https://www.luogu.com.cn/problem/P1443
题意:给你一个n×m的棋盘,然后给你马的起始位置(x,y),问题马到棋盘上所有点的最小步数,到达不了的就是-1。
思路:首先我们知道马能走日,把它的坐标模拟出来就可以了。
#include <bits/stdc++.h>
using namespace std;
int n, m, x, y;
queue<pair<int, int> > q;
int mp[410][401];
bool vis[410][410];
int dx[8] = {2,-2,2,-2,-1,1,-1,1}, dy[8] = {1,1,-1,-1,2,2,-2,-2};
signed main()
{
cin >> n >> m >> x >> y;
memset(mp, -1, sizeof mp);
mp[x][y] = 0;
vis[x][y] = 1;
q.push({x, y});
while (!q.empty())
{
int x = q.front().first, y = q.front().second;
q.pop();
for (int i = 0; i < 8; i++)
{
int tx = x + dx[i], ty = y + dy[i];
if (tx > 0 && tx <= n && ty > 0 && ty <= m && vis[tx][ty] == 0)
{
vis[tx][ty] = 1;
q.push({tx, ty});
mp[tx][ty] = mp[x][y] + 1;
}
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
printf("%-5d", mp[i][j]);
cout << endl;
}
return 0;
}
2.好奇怪的游戏
题目链接:https://www.luogu.com.cn/problem/P1747
题意:给了你两匹马的坐标(x1,y1)(x2,y2),它们可以走日也可以走田,让你求它们到(1,1)的最小步数。
思路:同上
#include <bits/stdc++.h>
using namespace std;
int dx[12] = {2,2,-2,-2,-1,-1,1,1,-2,-2,2,2};
int dy[12] = {2,-2,2,-2,-2,2,-2,2,1,-1,1,-1};
struct Node
{
int x, y, step;
};
queue<Node> q;
bool vis[25][25];
int nx, ny;
int bfs(int x, int y)
{
q.push(Node{x, y, 0});
vis[x][y] = 1;
while (!q.empty())
{
Node p = q.front(); q.pop();
for (int i = 0; i < 12; i ++)
{
int tx = p.x + dx[i], ty = p.y + dy[i];
if (tx > 0 && ty > 0 && tx <= 50 && ty <= 50 && vis[tx][ty] == 0)
q.push((Node){tx, ty, p.step + 1}), vis[tx][ty] = 1;
if (tx == nx && ty == ny) return p.step + 1;
}
}
}
int main()
{
cin >> nx >> ny;
cout << bfs(1, 1) << endl;
memset(vis, 0, sizeof vis);
while (!q.empty()) q.pop();
cin >> nx >> ny;
cout << bfs(1, 1) << endl;
return 0;
}
3.血色先锋队
题目链接:https://www.luogu.com.cn/problem/P1332
题意:地图是一个n×m的矩阵,然后有a个感染源,告诉了你a个感染源的位置,b个领主,让你求b个领主被感染的最短时间。
思路:就是找到a个感染源的位置,进行扩散,记录b个领主第一次被感染的时间即可。在搜索的时候,把这些感染源压入队列中,然后进行宽搜。
#include <bits/stdc++.h>
using namespace std;
int n, m, a, b;
bool vis[510][510];
int num[510][510];
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
struct Node
{
int x, y;
};
queue<Node> q;
int main()
{
cin >> n >> m >> a >> b;
for (int i = 0; i < a; i++)
{
int x, y; cin >> x >> y;
q.push(Node{x, y,});
vis[x][y] = 1;
}
while (!q.empty())
{
Node cur = q.front(); q.pop();
for (int i = 0; i < 4; i++)
{
int tx = cur.x + dx[i], ty = cur.y + dy[i];
if (tx > 0 && tx <= n && ty > 0 && ty <= m && !vis[tx][ty])
{
q.push(Node{tx, ty});
vis[tx][ty] = 1;
num[tx][ty] = num[cur.x][cur.y] + 1;
}
}
}
for (int i = 0; i < b; i++)
{
int x, y; cin >> x >> y;
cout << num[x][y] << endl;
}
return 0;
}
4. [USACO19JAN]Icy Perimeter S
题目链接:https://www.luogu.com.cn/problem/P5198
题意:给你一个n×n的图,如果‘#’是挨到一起的(指它的东南西北),那么认为它们是一个整体,让你求这些不同的整体的最大面和最大周长,如果面积相同,找周长最小的那个。
思路:首先我们知道,一个方格的面积是1,周长是4,如果有重叠,面积只需要累加,而两个连到一起,周长就会减少2,如果上下左右都有,那么周长就会减4,那么我们就可以针对某一个‘#‘来看,遍历它的四个方向,如果其中某一个位置不存在一个’#‘,那么周长就加一。
AC代码:
#include <bits/stdc++.h>
using namespace std;
int n;
char mp[1010][1010];
bool vis[1010][1010];
int max_s, min_c, s, c;
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
void dfs(int x, int y)
{
if (vis[x][y]) return ;
vis[x][y] = 1;
s ++;
for (int i = 0; i < 4; i++)
{
int tx = x + dx[i], ty = y + dy[i];
if (tx < 1 || tx > n || ty < 1 || ty > n || mp[tx][ty] == '.') c ++;
if (mp[tx][ty] == '#') dfs(tx, ty);
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> (mp[i] + 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
if (mp[i][j] == '#' && !vis[i][j])
{
s = 0, c = 0;
dfs(i, j);
if (s > max_s) max_s = s, min_c = c;
else if (s == max_s) min_c = min(min_c, c);
}
}
cout << max_s << ' ' << min_c << endl;
return 0;
}
5.单词接龙
题目链接:https://www.luogu.com.cn/problem/P1019
题意:给你n个字符串,让你找到它们能接成的最长的字符串,求其长度。每个字符串最多能使用两次,每个字符串之间是不能完全包含的。并且给出了你起始的字母。
思路:最先看到这个题的时候没得啥子思路,后面想了一下,你要连接成一个最长的字符出最先开始,你首先肯定要选两个来连接,那我们可以先把所有可以连接的两个字符串来接起来,并且记录它们的长度,并且都是第一个接到第二个后面,这跟我们后面的连接也并不冲突,所以我们用一个二维的数组连表示连接第i个字符串和第j个字符串组成的新的字符串,原字符串的长度,意思就是减去重合部分。
int cal(int x, int y)
{
bool flag = true;
int idx_y = 0;
for (int i = str[x].size() - 1; i >= 0; i--)
{
for (int j = i; j < str[x].size(); j++)
{
if (str[x][j] != str[y][idx_y ++])
{
flag = false;
break;
}
}
if (flag == true)
return str[x].size() - i;
idx_y = 0;
flag = true;
}
return 0;
}
经过上面的步骤,我们得到了连接任意两个字符串可以得到的长度。然后题目中给你了起始的字母,这个时候我们需要循环找到起始字母是给定的那个字母,以它开始进行dfs,在dfs中,有三种情况是不满足的,1.完全包含关系。2.连接这两个的形成的新字符串的长度为0。3.某个字符串使用的次数超过两次。后面的就是回溯dfs了。
void dfs(int x)
{
bool flag = false;
for (int i = 1; i <= n; i++)
{
if (vis[i] >= 2) continue;
if (overlap[x][i] == 0) continue;
if (overlap[x][i] == str[x].size() || overlap[x][i] == str[i].size()) continue;
temp += str[i].size() - overlap[x][i];
vis[i] ++;
flag = true;
dfs(i);
temp -= str[i].size() - overlap[x][i];
vis[i] --;
}
if (flag == false) ans = max(ans, temp);
return ;
}
AC代码
#include <bits/stdc++.h>
using namespace std;
int n;
string str[30];
int overlap[30][30], vis[30];
char ch;
int ans = -1;
int temp = 0;
int cal(int x, int y)
{
bool flag = true;
int idx_y = 0;
for (int i = str[x].size() - 1; i >= 0; i--)
{
for (int j = i; j < str[x].size(); j++)
{
if (str[x][j] != str[y][idx_y ++])
{
flag = false;
break;
}
}
if (flag == true)
return str[x].size() - i;
idx_y = 0;
flag = true;
}
return 0;
}
void dfs(int x)
{
bool flag = false;
for (int i = 1; i <= n; i++)
{
if (vis[i] >= 2) continue;
if (overlap[x][i] == 0) continue;
if (overlap[x][i] == str[x].size() || overlap[x][i] == str[i].size()) continue;
temp += str[i].size() - overlap[x][i];
vis[i] ++;
flag = true;
dfs(i);
temp -= str[i].size() - overlap[x][i];
vis[i] --;
}
if (flag == false) ans = max(ans, temp);
return ;
}
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> str[i];
cin >> ch;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
overlap[i][j] = cal(i, j);
for (int i = 1; i <= n; i++)
{
if (str[i][0] == ch)
{
vis[i] ++;
temp = str[i].size();
dfs(i);
vis[i] = 0;
}
}
cout << ans << endl;
return 0;
}
6.CF510B Fox And Two Dots
题目链接:https://www.luogu.com.cn/problem/CF510B
题意:给你一个n×m的棋盘,然后棋盘上有不同颜色的点,问你是否存在一个环,这个环的颜色相同。
思路:我们想一下,怎样才能构成一个点,首先颜色相同,其次是,我们在搜索的时候,最后一个点肯定是起点,只有满足上面两个点,才说明能够构成一个环。
那么我们在搜索的时候,对于每一个点,我们需要确定,此时位置的点的坐标和这个点是由那个点变过来的,因为我们在bfs的时候,可能会存在这样的情况,就是新访问的这个点,可能是上一个点的上一个点,显然这种情况我们应该继续搜素。所以对于每一个点和bfs我们就该这么写:
struct Node
{
int x, y;
int lx, ly;
};
void bfs(int x, int y)
{
q.push({x, y, -1, -1});
while (!q.empty())
{
Node cur = q.front(); q.pop();
vis[cur.x][cur.y] = 1;
for (int i = 0; i < 4; i++)
{
int tx = cur.x + dx[i], ty = cur.y + dy[i];
if (tx < 1 || tx > n || ty < 1 || ty > m || (tx == cur.lx && ty == cur.ly)) continue;
if (mp[cur.x][cur.y] != mp[tx][ty]) continue;
if (vis[tx][ty])
{
flag = true;
break;
}
else q.push({tx, ty, cur.x, cur.y});
}
if (flag) break;
}
}
AC代码:
#include <bits/stdc++.h>
using namespace std;
int n, m;
char mp[55][55];
bool vis[55][55], flag;
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
struct Node
{
int x, y;
int lx, ly;
};
queue<Node> q;
void bfs(int x, int y)
{
q.push({x, y, -1, -1});
while (!q.empty())
{
Node cur = q.front(); q.pop();
vis[cur.x][cur.y] = 1;
for (int i = 0; i < 4; i++)
{
int tx = cur.x + dx[i], ty = cur.y + dy[i];
if (tx < 1 || tx > n || ty < 1 || ty > m || (tx == cur.lx && ty == cur.ly)) continue;
if (mp[cur.x][cur.y] != mp[tx][ty]) continue;
if (vis[tx][ty])
{
flag = true;
break;
}
else q.push({tx, ty, cur.x, cur.y});
}
if (flag) break;
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> (mp[i] + 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (!vis[i][j])
{
bfs(i, j);
if (flag)
{
puts("Yes");
return 0;
}
}
puts("No");
return 0;
}
7.[USACO1.5]八皇后 Checker Challenge
题目链接:https://www.luogu.com.cn/problem/P1219
题意:有一个n×n的棋盘,有n颗棋子,使得每一行每一列每一对角线只有一颗棋子。找到解数,并且前三行每一行输出n个数字,第i个数字表示第i行棋子放的位置。
思路:这就是个八皇后问题,只需要保证每一行每一列每一对角线只有一颗棋子就可。
#include <bits/stdc++.h>
using namespace std;
int n, total;
int a[100], b[100], c[100], d[100];
///分别表示行、列、左下到右上、左上到右下
void print()
{
if (total <= 2)
{
for (int k = 1; k <= n; k++)
cout << a[k] << ' ';
cout << endl;
}
total ++;
}
void dfs(int x)
{
if (x > n)
{
print();
return ;
}
else
{
for (int i = 1; i <= n; i++)
{
if ((!b[i]) && (!c[x + i]) && (!d[x - i + n]))
{
a[x] = i;
b[i] = 1;
c[x + i] = 1;
d[x - i + n] = 1;
dfs(x + 1);
b[i] = 0;
c[x + i] = 0;
d[x - i + n] = 0;
}
}
}
}
signed main()
{
cin >> n;
dfs(1);
cout << total;
return 0;
}
8.机器人搬重物
题目链接:https://www.luogu.com.cn/problem/P1126
题意:给你一个N×M的方格,有一个机器人,它能接受的指令有:向前移动一步、两步、三步,向左转,向右转,每一个指令都会消耗一秒钟,求机器人到达指定位置所需的最少时间。
思路:首先这个题肯定是一个搜索(至少我这个蒟蒻是这么想的),根据做了这么多的搜索题,没遇到一个搜索题,特别是bfs,我都会想一个问题,在队列中,每一个点它都该有什么状态,比如有些是到达这么点需要多少步,那么表示这个位置肯定需要它的坐标和到达这个点的步数三个变量,而这个题,根据它的操作步骤,我们可以确定需要的状态有:坐标位置,面向那个方向,以及到达这个状态需要的步数。因此我们用一个结构体来装这四个变量:
struct node
{
int x, y, face, step;
}
那么接着想,因为每一个点都有自己面向的方向,我们有因该如何来表示方向呢,这里我们可以用0 1 2 3 分别来表示西 东 北 南,我们这里首先来考虑一下每一个点的转向,如果一个点是面向西的,那么它向左转,就是面对南的,如果这个点是向东的,那么它向左转,就面向北了,以此类推(向右转也是如此),我们可以得到它们的转向数组:int tl[4] = {3, 2, 0, 1}, tr[4] = {2, 3, 1, 0};,我们得到了转向数组。
接下来我们看搜索的过程,首先机器人的三类操作,1.直走。2.向左转。3.向右转,那么我们就分别模拟三个过程即可。
补充一下:说一下dx[]和dy[]数组,因为我们操作中只能直走,那么每一个朝向向前走的坐标都是不一样的,所以这里我们要一一对应,比如此时点的朝向是西,那么它朝前面走,它的y坐标会-1,x坐标不改变。所以得到了dx[]dy[]数组:int dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0};
void bfs()
{
queue<node> q;
st.step = 0;
vis[st.x][st.y][st.face] = 1;
q.push(st);
while (!q.empty())
{
now = q.front(); q.pop();
if (now.x == en.x && now.y == en.y)
{
ans = now.step;
return ;
}
///直走
ne = now;
for (int i = 1; i <= 3; i++)
{
ne.x += dx[now.face], ne.y += dy[now.face];
if (ne.x < 1 || ne.y < 1 || ne.x >= n || ne.y >= m || mp[ne.x][ne.y]) break;
if (vis[ne.x][ne.y][ne.face]) continue;
vis[ne.x][ne.y][ne.face] = 1;
ne.step = now.step + 1;
q.push(ne);
}
///改变方向
ne = now, ne.step = now.step + 1;
ne.face = tl[now.face];
if (!vis[ne.x][ne.y][ne.face])
{
vis[ne.x][ne.y][ne.face] = 1;
q.push(ne);
}
ne.face = tr[now.face];
if (!vis[ne.x][ne.y][ne.face])
{
vis[ne.x][ne.y][ne.face] = 1;
q.push(ne);
}
}
}
这里说漏了一个条件,图中给你的是n×m的方格,但是机器人是一个点,他只能在格子之间的交叉点上走,并且一个格子是障碍物,它的四个角都是无法到达的。
AC代码
#include <bits/stdc++.h>
using namespace std;
///face= 0-西 1-东 2-北 3-南
int dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0};
int tl[4] = {3, 2, 0, 1}, tr[4] = {2, 3, 1, 0};
struct node
{
int x, y, face, step;
}st, en, now, ne;
int n, m, ans = -1;
int mp[55][55], vis[55][55][4];
void bfs()
{
queue<node> q;
st.step = 0;
vis[st.x][st.y][st.face] = 1;
q.push(st);
while (!q.empty())
{
now = q.front(); q.pop();
if (now.x == en.x && now.y == en.y)
{
ans = now.step;
return ;
}
///直走
ne = now;
for (int i = 1; i <= 3; i++)
{
ne.x += dx[now.face], ne.y += dy[now.face];
if (ne.x < 1 || ne.y < 1 || ne.x >= n || ne.y >= m || mp[ne.x][ne.y]) break;
if (vis[ne.x][ne.y][ne.face]) continue;
vis[ne.x][ne.y][ne.face] = 1;
ne.step = now.step + 1;
q.push(ne);
}
///改变方向
ne = now, ne.step = now.step + 1;
ne.face = tl[now.face];
if (!vis[ne.x][ne.y][ne.face])
{
vis[ne.x][ne.y][ne.face] = 1;
q.push(ne);
}
ne.face = tr[now.face];
if (!vis[ne.x][ne.y][ne.face])
{
vis[ne.x][ne.y][ne.face] = 1;
q.push(ne);
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
int x; cin >> x;
if (x) mp[i][j] = mp[i - 1][j] = mp[i][j - 1] = mp[i - 1][j - 1] = 1;
}
char ch;
cin >> st.x >> st.y >> en.x >> en.y >> ch;
if (ch == 'W') st.face = 0;
else if (ch == 'E') st.face = 1;
else if (ch == 'N') st.face = 2;
else st.face = 3;
bfs();
if (ans == -1) puts("-1");
else cout << ans << endl;
return 0;
}
9.[NOIP2002 提高组] 字串变换
题目链接:https://www.luogu.com.cn/problem/P1032
题意:一直两个字符串AB,现在想将A变成B,现在有几种变换规则,变换规则是A中的某个字串变成另外一种字符串,问你变换的最小步数。
思路:这个题怎么入手呢???直接进行搜索,题中让你寻找的是最小步数,那么就有一种可能,我们在变换的时候可能会变换到原来出现过的字符串,所以我们要用一个东西来表示某个字符串出现的次数,很自然我们就想到了map:map<string, int> ma;,这里就表示string出现的次数int,然后我们直接遍历,我们直接从当前字符串开始,以第i个字符开始,采用次j中转换方式,看是否能够得到答案,那么这里我们需要想一下转换的过程,我们是从当前字符串的第i个字母开始采用第j中转换方法对吧, 那么有两种情况是不满足的,就是i加上第j种方法的原始字符串的长度不能大于当前字符串的长度(这里没说得很清楚,看下面的代码就明白了),如果满足了上面的条件,如果中间的某个字母不一样也是不满足条件的(看下面的代码),如果都满足,我们就可以进行复制了:
string trans(const string &str, int i, int j)
{
string ans = "";
if (i + org[j].length() > str.length()) return ans;
for (int k = 0; k < org[j].length(); k++)
if (str[i + k] != org[j][k]) return ans;
ans = str.substr(0, i);
ans += tran[j];
///将后面不一样的复制下来
ans += str.substr(i + org[j].length());
return ans;
}
上面我们已经考虑完转换过程了,我们又继续考虑搜索的过程,搜索过程也考虑完了的,就来看一下搜索的代码:
void dfs()
{
queue<node> q;
q.push((node){a, 0});
while (!q.empty())
{
node u = q.front(); q.pop();
string temp;
if (ma.count(u.str) == 1) continue;
if (u.str == b)
{
ans = u.steps;
break;
}
ma[u.str] = 1;
for (int i = 0; i < u.str.length(); i++)
{
for (int j = 0; j < n; j++)
{
temp = trans(u.str, i, j);
if (temp != "")
q.push((node){temp, u.steps + 1});
}
}
}
if (ans > 10 || ans == 0)
puts("NO ANSWER!");
else
cout << ans << endl;
}
AC代码:
#include <bits/stdc++.h>
using namespace std;
struct node
{
string str;
int steps;
};
string a, b;
string org[15], tran[15];
int n, ans;
map<string, int> ma;
string trans(const string &str, int i, int j)
{
string ans = "";
if (i + org[j].length() > str.length()) return ans;
for (int k = 0; k < org[j].length(); k++)
if (str[i + k] != org[j][k]) return ans;
ans = str.substr(0, i);
ans += tran[j];
///将后面不一样的复制下来
ans += str.substr(i + org[j].length());
return ans;
}
void dfs()
{
queue<node> q;
q.push((node){a, 0});
while (!q.empty())
{
node u = q.front(); q.pop();
string temp;
if (ma.count(u.str) == 1) continue;
if (u.str == b)
{
ans = u.steps;
break;
}
ma[u.str] = 1;
for (int i = 0; i < u.str.length(); i++)
{
for (int j = 0; j < n; j++)
{
temp = trans(u.str, i, j);
if (temp != "")
q.push((node){temp, u.steps + 1});
}
}
}
if (ans > 10 || ans == 0)
puts("NO ANSWER!");
else
cout << ans << endl;
}
signed main()
{
cin >> a >> b;
while (cin >> org[n] >> tran[n]) n ++;
dfs();
return 0;
}
10.砝码称重
题目链接:https://www.luogu.com.cn/problem/P1441
题意:n个砝码,重量分别为a_i,去掉m个砝码后,问最多能称量出多少不同的重量。
思路:记忆化搜索。搜索+完全背包。搜索过程其实好像,对于每一个砝码选择丢还是不丢即可,然后在找可以称量几种重量,就是个完全背包的过程。
#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[25];
bool vis[25],f[2010];
int tot, ans, res;
void dp()
{
memset(f, 0, sizeof f);
f[0] = true;
ans = 0, tot = 0;
for (int i = 0; i < n; i++)
{
if (vis[i]) continue;
for (int j = tot; j >= 0; j --)
if (f[j] && !f[j + a[i]])
f[j + a[i]] = 1, ans ++;
tot += a[i];
}
res = max(res, ans);
}
void dfs(int idx, int now)
{
if (now > m)
return ;
if (idx == n)
{
if (now == m)
dp();
return ;
}
dfs(idx + 1, now);
vis[idx] = 1;
dfs(idx + 1, now + 1);
vis[idx] = 0;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> a[i];
dfs(0, 0);
cout << res << endl;
return 0;
}
kuangbin
1.棋盘问题 (POJ - 1321)
题意:现在给了你一个n×n棋盘,只有当前位置是‘.'的时候才能摆放棋子,现在给了你一个k个棋子,问你有几种方法,任意两个棋子不能放在同一行或者同一列。
思路:就简单的bfs
#include <iostream>
using namespace std;
int n, k;
char mp[10][10];
int vis[10];
int cont = 0, way = 0;
void dfs(int h)
{
if (way == k) {cont ++; return ;};
if (h >= n) return ;
for (int i = 0; i < n; i++)
{
if (!vis[i] && mp[h][i] == '#')
{
vis[i] = 1;
way ++;
dfs(h + 1);
vis[i] = 0;
way --;
}
}
dfs(h + 1);
}
int main()
{
while (cin >> n >> k)
{
if (n == -1 && k == -1) break;
for (int i = 0; i < n; i++) cin >> mp[i];
dfs(0);
cout << cont << endl;
memset(vis, 0, sizeof vis);
cont = 0;
way = 0;
}
return 0;
}
2.Dungeon Master(POJ - 2251)
题意:给了你个三维地图,你可以上下左右前后的走,不能对角线的走,然后’S’是起点,'E’是终点,问你是否能够从起点走到节点。
题解:首先这是一个三维的图!!!!太**坑了。这里的坑主要是不好确定走的坐标,如图。

如上图,假设红点就是当前点,上下左右前后的坐标一次是(0, 0,1)(0, 0, -1)(1, 0, 0)(-1, 0, 0)(1, 0, 0)(-1, 0, 0),这个题难一点的就是考虑坐标。然后输入的样例给的是一个三维的,每一个是每一层的平面图。AC代码
/*
这是一个三维的地图,给你的是平面图
1.找到坐标,北、南、西、东、上、下
2.定义一个结构体,保存坐标和步数,这里的步数是指到达这个位置的步数
3.输入地图,并且找到起点
4.在搜索部分:遍历每个点的六个方向,找到终点就跳出
*/
#include <iostream>
#include <string>
#include <queue>
#include <cstring>
using namespace std;
int l, r, c;
char mp[35][35][35];
bool vis[35][35][35];
int direct[6][3] = {{0, 0, 1}, {0, 0, -1}, {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}};
typedef struct
{
int x, y, z;
int step;
}pos;
pos s;
int bfs(pos s)
{
queue<pos> q;
q.push(s);
while (!q.empty())
{
pos cur = q.front(); q.pop();
for (int i = 0; i < 6; i++)
{
pos new_pos;
new_pos.x = cur.x + direct[i][0];
new_pos.y = cur.y + direct[i][1];
new_pos.z = cur.z + direct[i][2];
new_pos.step = cur.step + 1;
if (!vis[new_pos.x][new_pos.y][new_pos.z] && new_pos.x >= 1 && new_pos.x <= l && new_pos.y >= 1 && new_pos.y <= r && new_pos.z >= 1 && new_pos.z <= c && mp[new_pos.x][new_pos.y][new_pos.z] != '#')
{
vis[new_pos.x][new_pos.y][new_pos.z] = 1;
if (mp[new_pos.x][new_pos.y][new_pos.z] == 'E') return new_pos.step;
q.push(new_pos);
}
}
}
return -1;
}
int main()
{
while (cin >> l >> r >> c)
{
if (l == 0 && r == 0 && c == 0) break;
memset(vis, 0, sizeof vis);
for (int i = 1; i <= l; i ++)
for (int j = 1; j <= r; j ++)
for (int k = 1; k <= c; k ++)
{
cin >> mp[i][j][k];
if (mp[i][j][k] == 'S')
s.x = i, s.y = j, s.z = k, s.step = 0;
}
int t = bfs(s);
if (t != -1) printf("Escaped in %d minute(s).\n",t);
else puts("Trapped!");
}
return 0;
}
3.Catch That Cow (POJ- 3278)
题意:给你一维的图,然后有个位置有一头牛,你要去抓住它,这个牛不动,然后你有两种移动方式,第一种:x = x - 1, x = x + 1。第二种:x = 2 ×x。问你抓住这头牛需要多长时间。
思路:这个题直接模拟他走的方式,然后进行bfs就可以了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N = 200010;
int n, k;
int dis[N];
bool vis[N];
void bfs(int x)
{
queue<int> q;
dis[n] = 0;
vis[n] = 1;
q.push(x);
while (!q.empty())
{
int t = q.front(); q.pop();
if (t == k){cout << dis[k]; return ;}
int x = t + 1;
if (x >= 0 && x <= 100000 && !vis[x])
{
vis[x] = 1;
dis[x] = dis[t] + 1;
q.push(x);
}
x = t - 1;
if (x >= 0 && x <= 100000 && !vis[x])
{
vis[x] = 1;
dis[x] = dis[t] + 1;
q.push(x);
}
x = t * 2;
if (x >= 0 && x <= 100000 && !vis[x])
{
vis[x] = 1;
dis[x] = dis[t] + 1;
q.push(x);
}
}
}
int main()
{
cin >> n >> k;
bfs(n);
return 0;
}
4.Fliptile(POJ - 3279)
题意:给你一个n×m的网格,有些方格是黑色,有些方格是白色,翻动一个方格,它的四周的方格也会跟着翻。黑色为1,白色为0。现在要将所有的方格都翻成白色,求最小的翻转次数,如果不行,输出“IMPOSSIBLE”。
思路:这个题是个状态转移。细想一下,如果一个方格翻转偶数次,那么它的颜色还是原来的样子,如果翻动奇数次才会改变颜色。那么在想一下,我们如果随便找一个方格翻,那么它的四周方格的颜色有可能也会改变,这样就会进入一个死循环,那么怎样才能找到最佳的方案。我们再想一下,假设我们确定了第一行的状态,那么我们怎样才能使第一行全都变为白色,是不是只能改变第二行,通过翻转第二行的方格来改变第一行的方格的颜色。那么怎样又才能确定是否能够将所有的方格都变成白色呢?假设我们现在该翻转倒数第二行的方格了,现在前面的所有方格都已经变为了白色,要让倒数第二行全部变为白色,我们是不是要翻转最后一行,如果能够将所有的格子都变为白色,那么我们在通过翻转最后一行让倒数第二行都变为白色后,最后一行肯定都是白色的,不然情况就不存在。
我们通过遍历第一行的情况来看,是否存在一种情况,能够让所有的方格都变为白色,通过二进制的方式,二进制位中,我们用0表示白色,1表示黑色,第一行的情况一共有(1<<m)种,我们就在这所有情况中找,是否存在一种情况来使所有的方格都变为白色。
这里我们用一个tmp[N][N]的二维数组来表示某个位置翻转后图的情况,mp[N][N]来表示原来的图,其实这里我们只需要遍历它的上一行,但是我们还是遍历它的上下左右吧,毕竟我最开始写了dx[]dy[]数组。
///翻转(x,y)这个位置
bool check(int x, int y)
{
int temp = mp[x][y];
for (int i = 0; i < 5; i++)
{
int tx = x + dx[i], ty = y + dy[i];
if (tx >= 1 && tx <= n && ty >= 1 && ty <= m) temp += tmp[tx][ty];
}
return temp & 1;
///这里我们用tmp&1来判断它上面是不是1,这里其实它的左右下都是0,因为当前这一行我们根本还没改变,这一行的改变只会由下一行的翻转来影响。当前这一行全是0。
}
然后我们在dfs中,我们需要做的是,1.找到前n-1行那些位置需要翻转,0次或者1次,然后最后一行我们需要特判,如果最后一行还有不是白色的话肯定就不行了。如果最后一行可以,我们在找到所有的翻转的次数。
int dfs()
{
for (int i = 2; i <= n; i++)
for (int j = 1; j <= m; j++)
if (check(i - 1, j)) tmp[i][j] = 1;
for (int i = 1; i <= m; i++)
if (check(n, i)) return inf;
int temp = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
temp += tmp[i][j];
return temp;
}
AC代码
/*
最终的结果只会由第一行的结果来确定,我们用二进制的方式来遍历第一行反转后的情况,根据这个结果来判断是否满足条件
如果满足条件,那么在反转结束后,最后一行也会全是0,所以我们在最后只需要特判最后一行的情况
*/
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
const int inf = 0x3f3f3f3f;
int ans;
int mp[25][25], s[25][25], tmp[25][25];
///s数组是用来保存答案的数组。
///这里需要考虑自身的情况
////特别说明一下,这里为什么dxdy数组是从00开始,因为我们在主函数中我们是通过第(i,j)这个位置来寻找i-1行的。
int dx[5] = {0, -1, 1, 0, 0}, dy[5] = {0, 0, 0, -1, 1};
int n, m;
bool check(int x, int y)
{
int temp = mp[x][y];
///其实这里就只遍历了i-1这一行,因为之前行全部都已经是白色了
for (int i = 0; i < 5; i++)
{
int tx = x + dx[i], ty = y + dy[i];
if (tx >= 1 && tx <= n && ty >= 1 && ty <= m) temp += tmp[tx][ty];
}
return temp & 1;
}
int dfs()
{
for (int i = 2; i <= n; i++)
for (int j = 1; j <= m; j++)
if (check(i - 1, j)) tmp[i][j] = 1;///(i,j)这个位置需要翻转。翻转完之后,i-1这一行肯定全都已经变成了白色了。
for (int i = 1; i <= m; i++)
if (check(n, i)) return inf;
int temp = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
temp += tmp[i][j];
return temp;
}
int main()
{
ans = inf;
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) cin >> mp[i][j];
for (int i = 0; i < (1 << m); i++) ///枚举的是反转完之后第一行的情况
{
memset(tmp, 0, sizeof tmp);
///这一步是将tmp数组的第一行变为我们遍历的二进制形式
for (int j = 1; j <= m; j++)
tmp[1][m - j + 1] = (i >> (j - 1)) & 1;
int cnt = dfs();
if (cnt < ans)
{
ans = cnt;
memcpy(s, tmp, sizeof tmp);
}
}
if (ans == inf) {puts("IMPOSSIBLE"); return 0;}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
cout << s[i][j] << ' ';
cout << endl;
}
return 0;
}
5.Find The Multiple(POJ - 1426)
题意:给你一个数字,然后让你找出最小的数,这个数只能由01组成,这个数恰好能够整出给你的这个数字。
思路:题中说了,这个数字只能由01合成,那么是不是每次这个数要么乘十,要么乘十加一,并且题目中说了数字不超过20位,那么我们可以直接深搜。
#include <iostream>
#define int long long
using namespace std;
int n;
bool flag;
void dfs(int num, int x)
{
if (num > 19 || flag == 1) return ;///注意这里的flag
if (x % n == 0)
{
flag = 1;
cout << x << endl;
return ;
}
dfs(num + 1, x * 10);
dfs(num + 1, x * 10 + 1);
}
signed main()
{
while (cin >> n)
{
if (n == 0) break;
flag = 0;
dfs(1, 1);
}
return 0;
}
6.Prime Path (POJ - 3126)
题意: 给你两个四位的素数a,b,你每次可以改变a的一位数,问你最少需要多少次,才能够使a变成b,在改变的过程中,改变一位后,a任然是素数。
思路:四位数的范围是1000-9999,所以首先找到这个范围内的所有素数,然后直接bfs来找即可。这里是有顺序的,先改变个位,十位,百位,千位。还有两个点,对于个位,只要个位不是偶数,其他的数字都有可能。对于千位,千位不能是0。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e6 + 10;
int prime[N], idx;
bool root[N];
void init()
{
idx = 0;
memset(root, 0, sizeof root);
root[1] = 1;
for (int i = 2; i < N; i++)
{
if (!root[i]) prime[idx ++] = i;
for (int j = 0; j < idx && i * prime[j] < N; j++)
{
root[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
int num1, num2;
int ans;
bool vis[N];
struct node
{
int num, step;
}now, ne;
int bfs(int x)
{
memset(vis, 0, sizeof vis);
queue<node> q;
vis[x] = 1;
q.push(node{x, 0});
while (!q.empty())
{
now = q.front(); q.pop();
if (now.num == num2) {return now.step;}
for (int i = 1; i <= 9; i += 2)
{
int num = now.num / 10 * 10 + i;
if (!root[num] && !vis[num])
{
ne.num = num;
ne.step = now.step + 1;
vis[num] = 1;
q.push(ne);
}
}
for (int i = 0; i <= 9; i++)
{
int num = now.num % 10 + (now.num / 100 * 10 + i) * 10;
if (!root[num] && !vis[num])
{
ne.num = num;
ne.step = now.step + 1;
vis[num] = 1;
q.push(ne);
}
}
for (int i = 0; i <= 9; i++)
{
int num = now.num % 100 + (now.num / 1000 * 10 + i) * 100;
if (!root[num] && !vis[num])
{
ne.num = num;
ne.step = now.step + 1;
vis[num] = 1;
q.push(ne);
}
}
for (int i = 1; i <= 9; i++)
{
int num = now.num % 1000 + i * 1000;
if (!root[num] && !vis[num])
{
ne.num = num;
ne.step = now.step + 1;
vis[num] = 1;
q.push(ne);
}
}
}
return -1;
}
int main()
{
init();
int t; cin >> t;
while (t --)
{
cin >> num1 >> num2;
int ans = bfs(num1);
if (ans >= 0) cout << ans << endl;
else cout << "Impossible" << endl;
}
return 0;
}
7.Shuffle’m Up(POJ - 3087)
题目连接:https://vjudge.net/problem/POJ-3087
题意:给了你两堆排s1和s2,现在要将两副牌合成一堆,规则是s2在下,s1在上,一次这么合并,合并了在分成两堆,新的s1是从最下面取,s2是从最上面取,然后给你一个答案序列c,问你是否能够通过上面的变换,在合并成一副排后的序列是给你的序列,如果能,输出最小的步数,如果不能输出-1。
思路:
8.Fire Game(FZU - 2150)
题目链接:https://vjudge.net/problem/FZU-2150
题意:告诉你有两个人要去点火堆,意思就是选择两个点来点,然后每个火堆可以蔓延到它的上下左右四个位置,问你是否能够将所有的草堆都点燃完,如果能,输出最小的步数,不行就输出‘-1’。
思路:题目中说明了两个人去点,相当于是只能点两次,这个位置可以是相同的位置,也可以是不同的位置,那么说明连通的草堆最多就只有两堆,如果多了就会有草堆不能被点。那么我们可以知道这是一个双端的bfs,从两个方向开始找,那么就是四层循环。那么我们首先要找到连通块的数量。这里我们需要一个belong数组来保存(i,j)这个位置的草堆是属于哪一个连通块,因为后面我们在循环起点的时候需要判断两个起点是属于哪一个连通块。(下面会解释)
void find_num(int x, int y)
{
queue<node> q;
q.push({x, y, 0});
block_num ++;
while (!q.empty())
{
node now = q.front(); q.pop();
belong[now.x][now.y] = block_num;
for (int i = 0; i < 4; i++)
{
node cur;
cur.x = now.x + dx[i], cur.y = now.y + dy[i], cur.step = now.step + 1;
if (cur.x >= 1 && cur.x <= n && cur.y >= 1 && cur.y <= m && !belong[cur.x][cur.y] && mp[cur.x][cur.y] == '#')
{
belong[cur.x][cur.y] = block_num;
q.push(cur);
}
}
}
}
好,加入我们确定了连通块的数量是<=2的,那么我们接下来就需要遍历两个起点了,这里我们用四层循环来找,第一个起点是属于第一个连通块的,然后第二个连通块就=block_num,这里的block_num要么等于1要么等于2,都是满足条件的,我们在每次找到的时候在答案里面去一个min就可以找到最小值。
if (block_num > 2) ans = -1;
else
{
node s1, s2;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (belong[i][j] == 1)
{
s1.step = 0, s1.x = i, s1.y = j;
for (int k = 1; k <= n; k ++)
for (int l = 1; l <= m; l++)
if (belong[k][l] == block_num)
{
s2.step = 0, s2.x = k, s2.y = l;
ans = min(ans, double_bfs(s1, s2));
}
}
}
那么我们剩下的就只需要bfs一下起点就可以了,我们在这里要用一个结构体来存每一个点的坐标以及到达这个位置所需要的步数,并且这里的步数一定是最小的步数。
struct node
{
int x, y;
int step;
};
接下来就是bfs起点了,我们要找的是最小的时间,也就是最小的步数,在bfs里面我们需要一个变量cost来记录需要的最大步数。
int double_bfs(node s1, node s2)
{
memset(vis, 0, sizeof vis);
int cost = 0;
queue<node> q;
q.push(s1), q.push(s2);
vis[s1.x][s1.y] = 1, vis[s2.x][s2.y] = 1;
while (!q.empty())
{
node tmp = q.front(); q.pop();
cost = tmp.step;
for (int i = 0; i < 4; i++)
{
node cur;
cur.x = tmp.x + dx[i], cur.y = tmp.y + dy[i], cur.step = tmp.step + 1;
if (cur.x >= 1 && cur.x <= n && cur.y >= 1 && cur.y <= m && !vis[cur.x][cur.y] && mp[cur.x][cur.y] == '#')
{
vis[cur.x][cur.y] = 1;
q.push(cur);
}
}
}
return cost;
}
AC代码
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
struct node
{
int x, y;
int step;
};
int ans, cnt;
int n, m;
char mp[15][15];
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
int block_num = 0;
int belong[15][15];
bool vis[15][15];
void find_num(int x, int y)
{
queue<node> q;
q.push({x, y, 0});
block_num ++;
while (!q.empty())
{
node now = q.front(); q.pop();
belong[now.x][now.y] = block_num;
for (int i = 0; i < 4; i++)
{
node cur;
cur.x = now.x + dx[i], cur.y = now.y + dy[i], cur.step = now.step + 1;
if (cur.x >= 1 && cur.x <= n && cur.y >= 1 && cur.y <= m && !belong[cur.x][cur.y] && mp[cur.x][cur.y] == '#')
{
belong[cur.x][cur.y] = block_num;
q.push(cur);
}
}
}
}
int double_bfs(node s1, node s2)
{
memset(vis, 0, sizeof vis);
int cost = 0;
queue<node> q;
q.push(s1), q.push(s2);
vis[s1.x][s1.y] = 1, vis[s2.x][s2.y] = 1;
while (!q.empty())
{
node tmp = q.front(); q.pop();
cost = tmp.step;
for (int i = 0; i < 4; i++)
{
node cur;
cur.x = tmp.x + dx[i], cur.y = tmp.y + dy[i], cur.step = tmp.step + 1;
if (cur.x >= 1 && cur.x <= n && cur.y >= 1 && cur.y <= m && !vis[cur.x][cur.y] && mp[cur.x][cur.y] == '#')
{
vis[cur.x][cur.y] = 1;
q.push(cur);
}
}
}
return cost;
}
int main()
{
int t; cin >> t;
for (int kk = 1; kk <= t; kk++) { ans = inf; block_num = 0; memset(belong, 0, sizeof belong); cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> (mp[i] + 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (!belong[i][j] && mp[i][j] == '#') find_num(i, j);
if (block_num > 2) ans = -1;
else
{
node s1, s2;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (belong[i][j] == 1)
{
s1.step = 0, s1.x = i, s1.y = j;
for (int k = 1; k <= n; k ++)
for (int l = 1; l <= m; l++)
if (belong[k][l] == block_num)
{
s2.step = 0, s2.x = k, s2.y = l;
ans = min(ans, double_bfs(s1, s2));
}
}
}
cout << "Case " << kk << ": " << ans << endl;
}
return 0;
}
9.Pots (POJ - 3414)
题目链接:https://vjudge.csgrandeur.cn/problem/POJ-3414
题意:现在有两个罐子,他们的大小分别为a升和b升,现在有三种操作,1.将罐子里面的水灌满。2.将罐子里的水倒空。3.将一个罐子里的水到入另一个罐子中。现在给你一个数字k,问经过怎样的操作,能最快的让其中一个罐子中水的体积到达k,输出最小的步数和操作的过程。
思路:
10.Fire! (UVA - 11624)
题目链接:https://vjudge.csgrandeur.cn/problem/UVA-11624
题意:给你一个n*m的二位字符数组(迷宫),其中‘F’表示火堆,‘#’表示墙,‘J’表示当前位置,‘.’表示可以走的点,其中火堆可以蔓延,可以到达除了火堆已经到达过的其他任何地方,而人只能到达‘.’的位置,然后问是否能在火烧到他之前到达迷宫的边缘。
思路:首先这里说了火堆能够蔓延,并且是能够到达火堆没到达过的其他任何地方,那么’.'和‘#’火堆都可以到达,那么我们先将火堆蔓延到其他每个的点的最短是时间记录下来,然后在比较人到达其他点的时间,与火堆到达的时间做比较,如果时间小于火堆到达的时间,说明这个点是可到达的。
这里相当于搜索了两遍,第一遍搜索是搜索的火堆到达其他点的时间,第二个搜索搜的时人到达其他点的时间。
这里有一个坑,题目中没有说明只有一个火堆,所以一个迷宫中可能含有多个火堆。
int fire[N][N];///用一个二位数组来表示火蔓延到其他点的时间,
///最开始fire数组要定义为inf,
///用一个结构体来表示到达每一个点的位置
struct node
{
int x, y;
int step;
}
寻找火堆可到达的每个点的最短时间
void fire_bfs()
{
memset(vis, 0, sizeof vis);
queue<node> q;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (mp[i][j] == 'F')
{
vis[i][j] = 1;
fire[i][j] = 0;
q.push({i, j, 0});
}
int x, y, step;
while (!q.empty())
{
node tmp = q.front(); q.pop();
for (int i = 0; i < 4; i++)
{
x = tmp.x + dx[i], y = tmp.y + dy[i], step = tmp.step + 1;
if (x < 1 || x > n || y < 1 || y > m || mp[x][y] == '#' || mp[x][y] == 'F' || vis[x][y])
continue;
vis[x][y] = 1;
fire[x][y] = step;
q.push({x, y, step});
}
}
}
寻找最短的步数
int bfs(int x, int y)
{
memset(vis, 0, sizeof vis);
queue<node> q;
q.push({x, y, 0});
vis[x][y] = 1;
while (!q.empty())
{
node now = q.front(); q.pop();
for (int i = 0; i < 4; i++)
{
node tmp;
tmp.x = now.x + dx[i], tmp.y = now.y + dy[i], tmp.step = now.step + 1;
if (tmp.x < 1 || tmp.x > n || tmp.y < 1 || tmp.y > m) return tmp.step;
if (fire[tmp.x][tmp.y] <= tmp.step || mp[tmp.x][tmp.y] == '#' || mp[tmp.x][tmp.y] == 'F' || vis[tmp.x][tmp.y])
continue;
vis[tmp.x][tmp.y] = 1;
q.push(tmp);
}
}
return 0;
}
AC代码
#include <bits/stdc++.h>
using namespace std;
struct node
{
int x, y, step;
};
const int inf = 0x3f3f3f3f;
const int N = 1010;
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
int n, m;
char mp[N][N];
bool vis[N][N];
int fire[N][N];
void fire_bfs()
{
memset(vis, 0, sizeof vis);
queue<node> q;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (mp[i][j] == 'F')
{
vis[i][j] = 1;
fire[i][j] = 0;
q.push({i, j, 0});
}
int x, y, step;
while (!q.empty())
{
node tmp = q.front(); q.pop();
for (int i = 0; i < 4; i++)
{
x = tmp.x + dx[i], y = tmp.y + dy[i], step = tmp.step + 1;
if (x < 1 || x > n || y < 1 || y > m || mp[x][y] == '#' || mp[x][y] == 'F' || vis[x][y])
continue;
vis[x][y] = 1;
fire[x][y] = step;
q.push({x, y, step});
}
}
}
int bfs(int x, int y)
{
memset(vis, 0, sizeof vis);
queue<node> q;
q.push({x, y, 0});
vis[x][y] = 1;
while (!q.empty())
{
node now = q.front(); q.pop();
for (int i = 0; i < 4; i++)
{
node tmp;
tmp.x = now.x + dx[i], tmp.y = now.y + dy[i], tmp.step = now.step + 1;
if (tmp.x < 1 || tmp.x > n || tmp.y < 1 || tmp.y > m) return tmp.step;
if (fire[tmp.x][tmp.y] <= tmp.step || mp[tmp.x][tmp.y] == '#' || mp[tmp.x][tmp.y] == 'F' || vis[tmp.x][tmp.y])
continue;
vis[tmp.x][tmp.y] = 1;
q.push(tmp);
}
}
return 0;
}
int main()
{
int t;
while (cin >> t)
{
while (t --)
{
memset(mp, 0, sizeof mp);
memset(vis, 0, sizeof vis);
int sx, sy;
cin >> n >> m;
getchar();
for (int i = 1; i <= n; i++)
{
cin >> (mp[i] + 1);
for (int j = 1; j <= m; j++)
{
fire[i][j] = inf;
if (mp[i][j] == 'J') sx = i, sy = j;
}
}
fire_bfs();
int cnt = bfs(sx, sy);
if (cnt) cout << cnt << endl;
else puts("IMPOSSIBLE");
}
}
return 0;
}
11.迷宫问题
题目链接:https://vjudge.net/problem/POJ-3984
题意:给你一个5*5的二维数组,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
思路:首先我们可以确定,起点和终点一定是0,并且一定是可到达的,那么我们可以直接从起点开始搜索,题目中要求找到最短路线并且将最短的路线输出出来,那么我们在搜索的过程中只需要将每个点是由那个点变换过来即可。现在的问题是我们怎样记录每个点是由那个点变换过来的即可,这里我们可以使用一个二维的结构来来表示,结构体里面装的是某个点的坐标,然后结构体的下标就表示的某个点。
struct node
{
int x, y;
}step[N][N];
那么比如step [ i ] [ j ] 就表示(i, j)这个点是由那个点变换过来的。然后后面就是正常的宽搜了。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
struct node
{
int x, y;
}step[10][10];
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
int mp[10][10];
bool vis[10][10];
void print(node p)
{
if (p.x == 0 && p.y == 0){ printf("(0, 0)\n"); return ;}///特别判断输出起点的位置,这里相当于是终点(寻找路径的终点,在什么位置停止)
print(step[p.x][p.y]);///运用递归来输出路劲,一直寻找当前位置的前一个位置
printf("(%d, %d)\n", p.x, p.y);
}
void bfs()
{
node a;
a.x = 0, a.y = 0;
vis[0][0] = 1;
queue<node> q;
q.push(a);
while (!q.empty())
{
a = q.front(); q.pop();
if (a.x == 4 && a.y == 4) {print(a); break ;}
for (int i = 0; i < 4; i ++)
{
node tmp;
tmp.x = a.x + dx[i], tmp.y = a.y + dy[i];
if (tmp.x >= 0 && tmp.x < 5 && tmp.y >= 0 && tmp.y < 5 && !vis[tmp.x][tmp.y] && mp[tmp.x][tmp.y] == 0)
{
vis[tmp.x][tmp.y] = 1;
step[tmp.x][tmp.y] = a;///保存当前点是由哪一个点又由来的
q.push(tmp);
}
}
}
}
int main()
{
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++) cin >> mp[i][j];
memset(vis, 0, sizeof 0);
bfs();
return 0;
}
12.非常可乐(HDU - 1495)
题目链接:https://vjudge.net/problem/HDU-1495
题意:给你一个总体积为s的可乐,然后现在有两个杯子,他们的体积分别为a和b,现在他们要平分可乐,问你是否能够平分。你可以将三个瓶子的可乐相互倒。
思路:题目告诉你,现在有两个人要喝可乐,有两个杯子,并且告诉了你它们的体积,题目中问你它们喝到的可乐的总量是否能一样,并且可乐要喝完。假设我们假设每个杯子都有可能,并且每个杯子现在还可以在装一定体积的可乐,那么每一个杯子都有两种倒法,三个杯子就有六种倒法,那我们直接枚举一遍即可。
初始化:我们需要一个结构体来记录当前的状态,记录遍历中3个水杯容藏可乐情况。
struct node
{
int v[5];
int step;
}tmp;
现在我们来想一下,如何模拟倒水的过程。假设我们现在要a杯子中的可乐倒入b杯子中去,那么现在存在的情况有:1.a杯子中的水能够全部倒入b中。2.a中的杯子不能全部倒入b中。此时,两个杯子中的可乐中的总量sum=a+b,如果sum大于b杯子的容量,那么b杯子可以装满,反之b杯子能装的可乐就等于sum,那么自然而然a杯子此时的体积就是sum-b杯子此时的体积。
void pour(int a, int b)
{
int sum = tmp.v[a] + tmp.v[b];
if (sum >= v[b]) tmp.v[b] = v[b];
else tmp.v[b] = sum;
tmp.v[a] = sum - tmp.v[b];
}
剩下的最后一一步便是搜索的过程了。搜索的关键就是模拟倒水的过程, 我们在倒水的,是在以每个状态基准上来倒的,每次倒可乐后,每个容器中的可乐都会更新。现在我们考虑一下搜索结束的状态,首先我们要想清楚,我们最先应该往容量小的容器中倒,我们看样例中的4 1 3这组数据,我们的倒法是,往a中倒两次,那么a得到的可乐是两毫升,还剩余两毫升,剩余的我们就全部倒入b容器中。所以我们在搜索中的结束条件是,if (cnt.v[1] == cnt.v[3] && cnt.v[2] == 0),我们默认了v[2]是容量最小的,v[3]是容量最大的,最终的装填是我们将剩余的所有可乐全部倒入v[3]中,就是容量最大的容器,而v[2]中不倒。(我们搜索的过程是,搜索某个容器往某个容器倒入了多少可乐。倒可乐只会有两种状态,要么全部倒完还不够或者刚好够,或者倒满了还有剩余。)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int v[5];
int ans;
bool vis[110][110][110];
struct node
{
int v[5];
int step;
}tmp;
void pour(int a, int b)
{
int sum = tmp.v[a] + tmp.v[b];
if (sum >= v[b]) tmp.v[b] = v[b];
else tmp.v[b] = sum;
tmp.v[a] = sum - tmp.v[b];
}
void bfs()
{
queue<node> q;
node cnt;
cnt.v[1] = v[1];
cnt.v[2] = 0;
cnt.v[3] = 0;
cnt.step = 0;
q.push(cnt);
memset(vis, 0, sizeof vis);
vis[v[1]][0][0] = 1;
while (!q.empty())
{
///cnt表示当前情况的水位情况
cnt = q.front(); q.pop();
if (cnt.v[1] == cnt.v[3] && cnt.v[2] == 0) {cout << cnt.step << endl; return ;}
for (int i = 1; i < 4; i++)
for (int j = 1; j < 4; j++)
if (i != j)
{
tmp = cnt;
pour(i, j);
if (!vis[tmp.v[1]][tmp.v[2]][tmp.v[3]])
{
tmp.step ++;
q.push(tmp);
vis[tmp.v[1]][tmp.v[2]][tmp.v[3]] = 1;
}
}
}
cout << "NO" << endl;
}
int main()
{
while (cin >> v[1] >> v[2] >> v[3])
{
if (v[1] == 0 && v[2] == 0 && v[3] == 0) break;
if (v[2] > v[3]) {int t = v[2]; v[2] = v[3]; v[3] = t;}
bfs();
}
return 0;
}
13.Find a way (HDU - 2612)
题目链接:https://vjudge.net/problem/HDU-2612#author=0
题意:给你一个n×m的地图,现在有两个人要见面,他们见面的地方有多个,找到一个见面的地方,使得两人见面所花的时间最短。
思路:先说一下错误思路,最开始的时候,我是从终点开始搜,分别去找两个人的位置,这样倒着去找会超时。那么我们换个想法,我们顺着找,我们在输入的时候记录两个人的位置,然后分别记录到达每个见面地点所需要的时间,记录完毕后我们只需要遍历每个见面地点所需要的时间然后取一个最小值即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
struct node
{
int x, y;
int step;
};
int n, m;
char mp[210][210];
bool vis[210][210];
int res[210][210];
int ans;
void bfs(int x, int y)
{
memset(vis, 0, sizeof vis);
queue<node> q;
q.push({x, y, 0});
vis[x][y] = 1;
while (!q.empty())
{
node cur = q.front(); q.pop();
for (int i = 0; i < 4; i++)
{
node tmp;
tmp.x = cur.x + dx[i], tmp.y = cur.y + dy[i], tmp.step = cur.step + 1;
if (tmp.x >= 1 && tmp.x <= n && tmp.y >= 1 && tmp.y <= m && !vis[tmp.x][tmp.y] && mp[tmp.x][tmp.y] != '#')
{
vis[tmp.x][tmp.y] = 1;
q.push(tmp);
res[tmp.x][tmp.y] += tmp.step;
}
}
}
}
signed main()
{
while (~scanf("%d%d", &n, &m))
{
ans = inf;
memset(res, 0, sizeof res);
int fx, fy, nx, ny;
for (int i = 1; i <= n; i++)
{
scanf("%s", (mp[i] + 1));
for (int j = 1; j <= m; j++)
{
if (mp[i][j] == 'Y') {fx = i, fy = j;}
if (mp[i][j] == 'M') {nx = i, ny = j;}
}
}
bfs(fx, fy);
bfs(nx, ny);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (mp[i][j] == '@' && res[i][j]) ans = min(ans, res[i][j]);
printf("%d\n", ans * 11);
}
return 0;
}
本文详细解析了洛谷和kuangbin平台上的编程题目,包括马的遍历、奇怪的游戏、血色先锋队等多个题目,涉及算法和数据结构,通过搜索和广度优先遍历等方法解决各类问题,适合程序员提升算法技能。
2789

被折叠的 条评论
为什么被折叠?



