(1)矩阵距离
给定一个 N 行 M 列的 01 矩阵 A,A[i][j] 与 A[k][l] 之间的曼哈顿距离定义为:
dist(A[i][j],A[k][l])=|i−k|+|j−l|
输出一个 N 行 M 列的整数矩阵 B,其中:
B[i][j]=min1≤x≤N,1≤y≤M,A[x][y]=1dist(A[i][j],A[x][y])
输入格式
第一行两个整数 N,M。
接下来一个 N 行 M 列的 01 矩阵,数字之间没有空格。
输出格式
一个 N 行 M 列的矩阵 B,相邻两个整数之间用一个空格隔开。
数据范围
1≤N,M≤1000
输入样例:
3 4
0001
0011
0110
输出样例:
3 2 1 0
2 1 0 0
1 0 0 1
代码
// //每个0到最近1的距离
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int,int> PII;
const int N=1005;
char g[N][N];
int d[N][N];
queue<PII> q;
int n,m;
void bfs(){
memset(d,-1,sizeof d);
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(g[i][j]=='1'){ //找到起点
d[i][j]=0;
q.push({i,j});
}
}
}
while(q.size()){
PII t=q.front();
q.pop();
for(int i=0;i<4;i++){ //搜索
int a=t.first+dx[i],b=t.second+dy[i];
if(a<0 || a>=n || b<0 || b>=m) continue; //是否越界
if(d[a][b]!=-1) continue; //是否走过
q.push({a,b});
d[a][b]=d[t.first][t.second]+1;
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>g[i][j];
}
}
bfs();
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
printf("%d ",d[i][j]);
}
puts("");
}
return 0;
}
(2)八数码
在一个 3×3 的网格中,1∼8 这 8 个数字和一个 x 恰好不重不漏地分布在这 3×3 的网格中。
例如:
1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x 与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
1 2 3
4 5 6
7 8 x
例如,示例中图形就可以通过让 x 先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3 1 2 3 1 2 3 1 2 3
x 4 6 4 x 6 4 5 6 4 5 6
7 5 8 7 5 8 7 x 8 7 8 x
现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。
输入格式
输入占一行,将 3×3 的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
输出格式
输出占一行,包含一个整数,表示最少交换次数。
如果不存在解决方案,则输出 −1。
输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19
代码:
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
using namespace std;
int bfs(string state)
{
queue<string> q;
unordered_map<string, int> d;
q.push(state);
d[state] = 0;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
string end = "12345678x";
while (q.size())
{
auto t = q.front();
q.pop();
if (t == end) return d[t];
int distance = d[t];
int k = t.find('x');
int x = k / 3, y = k % 3;
for (int i = 0; i < 4; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (a >= 0 && a < 3 && b >= 0 && b < 3)
{
swap(t[a * 3 + b], t[k]);
if (!d.count(t))
{
d[t] = distance + 1;
q.push(t);
}
swap(t[a * 3 + b], t[k]);
}
}
}
return -1;
}
int main()
{
char s[2];
string state;
for (int i = 0; i < 9; i ++ )
{
cin >> s;
state += *s;
}
cout << bfs(state) << endl;
return 0;
}
(3)魔板
Rubik 先生在发明了风靡全球的魔方之后,又发明了它的二维版本——魔板。
这是一张有 8 个大小相同的格子的魔板:
1 2 3 4
8 7 6 5
我们知道魔板的每一个方格都有一种颜色。
这 8 种颜色用前 8 个正整数来表示。
可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。
对于上图的魔板状态,我们用序列 (1,2,3,4,5,6,7,8) 来表示,这是基本状态。
这里提供三种基本操作,分别用大写字母 A,B,C 来表示(可以通过这些操作改变魔板的状态):
A:交换上下两行;
B:将最右边的一列插入到最左边;
C:魔板中央对的4个数作顺时针旋转。
下面是对基本状态进行操作的示范:
A:
8 7 6 5
1 2 3 4
B:
4 1 2 3
5 8 7 6
C:
1 7 2 4
8 6 3 5
对于每种可能的状态,这三种基本操作都可以使用。
你要编程计算用最少的基本操作完成基本状态到特殊状态的转换,输出基本操作序列。
注意:数据保证一定有解。
输入格式
输入仅一行,包括 8 个整数,用空格分开,表示目标状态。
输出格式
输出文件的第一行包括一个整数,表示最短操作序列的长度。
如果操作序列的长度大于0,则在第二行输出字典序最小的操作序列。
数据范围
输入数据中的所有数字均为 1 到 8 之间的整数。
输入样例:
2 6 8 4 5 7 3 1
输出样例:
7
BCABCCB
代码
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
using namespace std;
char g[2][4];
unordered_map<string, pair<char, string>> pre; //第一个参数,转换后的字符串,第二个参数转变到该字符串的步数是 多少
unordered_map<string, int> dist;//第一个参数,转换后的字符串,第二个参数,哪一步(A,B,C),第三个参数,转换前的字符串
void set(string state)
{
for (int i = 0; i < 4; i ++ ) g[0][i] = state[i];
for (int i = 7, j = 0; j < 4; i --, j ++ ) g[1][j] = state[i];
}
string get()
{
string res;
for (int i = 0; i < 4; i ++ ) res += g[0][i];
for (int i = 3; i >= 0; i -- ) res += g[1][i];
return res;
}
string move0(string state)
{
set(state);
for (int i = 0; i < 4; i ++ ) swap(g[0][i], g[1][i]);
return get();
}
string move1(string state)
{
set(state);
int v0 = g[0][3], v1 = g[1][3];
for (int i = 3; i > 0; i -- )
{
g[0][i] = g[0][i - 1];
g[1][i] = g[1][i - 1];
}
g[0][0] = v0, g[1][0] = v1;
return get();
}
string move2(string state)
{
set(state);
int v = g[0][1];
g[0][1] = g[1][1];
g[1][1] = g[1][2];
g[1][2] = g[0][2];
g[0][2] = v;
return get();
}
int bfs(string start, string end)
{
if (start == end) return 0;
queue<string> q;
q.push(start);
dist[start] = 0;
while (!q.empty())
{
auto t = q.front();
q.pop();
string m[3];
m[0] = move0(t);
m[1] = move1(t);
m[2] = move2(t);
for (int i = 0; i < 3; i ++ )
if (!dist.count(m[i]))
{
dist[m[i]] = dist[t] + 1;
pre[m[i]] = {'A' + i, t};
q.push(m[i]);
if (m[i] == end) return dist[end];
}
}
return -1;
}
int main()
{
int x;
string start, end;
for (int i = 0; i < 8; i ++ )
{
cin >> x;
end += char(x + '0');
}
for (int i = 1; i <= 8; i ++ ) start += char('0' + i);
int step = bfs(start, end);
cout << step << endl;
string res;
while (end != start)
{
res += pre[end].first;
end = pre[end].second;
}
reverse(res.begin(), res.end());
if (step > 0) cout << res << endl;
return 0;
}