842. 排列数字(dfs)
#include <iostream>
using namespace std;
const int N = 10;
int n, path[N];
void dfs(int u, int state) {
if (u == n) {
for (int i = 0; i < u; ++ i) {
cout << path[i] << ' ';
}
cout << endl;
return ;
}
for (int i = 1; i <= n; ++ i) {
if (!(state >> i & 1)) {
path[u] = i;
dfs(u + 1, state + (1 << i));
}
}
}
int main() {
cin >> n;
dfs(0, 0);
}
843. n-皇后问题(dfs)
- n* n放n个,因此一行放一个,dfs每一行中在哪个位置放皇后
- O ( N ! ) O(N!) O(N!)
- 关于对角线和斜对角线:显然由行和列可以得到一一对应的对角线,因此,可以推出公式 y = x + b y=x+b y=x+b和 y = − x + b y=-x+b y=−x+b,因此得到 b = y − x b=y-x b=y−x和 b = x + y b=x+y b=x+y,由于y-x(数组下标)可能是负数,因此加一个偏移量n防止下标小于0
- 注意数组大小要开两倍,因为对角线数量是横纵的两倍
#include <iostream>
using namespace std;
const int N = 20;
int n;
char g[N][N];
bool col[N], dg[N], udg[N];
void dfs(int x) {
if (x == n) {
for (int i = 0; i < n; ++ i) {
puts(g[i]);
}
puts("");
return ;
}
for (int y = 0; y < n; ++ y) {
if (!col[y] && !dg[y - x + n] && !udg[x + y]) {
col[y] = dg[y - x + n] = udg[x + y] = true;
g[x][y] = 'Q';
dfs(x + 1);
col[y] = dg[y - x + n] = udg[x + y] = false;
g[x][y] = '.';
}
}
}
int main() {
cin >> n;
for (int i = 0; i < n; ++ i)
for (int j = 0; j < n; ++ j)
g[i][j] = '.';
dfs(0);
}
844. 走迷宫(bfs)
#include <iostream>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
struct Node {
int x, y, dist;
};
int n, m;
char g[N][N];
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};
int bfs() {
queue<Node> que;
que.push({1, 1, 0});
g[1][1] = '1';
while (que.size()) {
auto t = que.front();
que.pop();
if (t.x == n && t.y == m) {
return t.dist;
}
for (int i = 0; i < 4; ++ i) {
int nx = t.x + dx[i], ny = t.y + dy[i];
if (nx < 1 || nx > n || ny < 1 || ny > m || g[nx][ny] == '1') continue;
g[nx][ny] = '1';
que.push({nx, ny, t.dist + 1});
}
}
return -1;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j)
cin >> g[i][j];
cout << bfs();
}
845. 八数码(bfs)
- 由于确定为3 * 3,给出所有数,可以得出它在方格中的位置为(k / 3, k % 3),而可以由 3 ∗ x + y 3 * x + y 3∗x+y由方格得到所有数中的位置。因此,可以先将x的所有数中坐标转换为方格中坐标,上下左右移动再转回方格中坐标
- “得到正确排列至少需要进行多少次交换“,已知初始状态和最终状态,求最少步数,bfs
- 这里用一个map来维护string的dist值,注意用
!dist.count(s)
来表示当前的s状态是第一次走到的,可以被放入queue和更新dist值,否则不更新不放入
#include <iostream>
#include <queue>
#include <unordered_map>
using namespace std;
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};
int bfs(string state) {
queue<string> que;
unordered_map<string, int> dist;
que.push(state);
dist[state] = 0;
string end = "12345678x";
while (que.size()) {
string s = que.front();
que.pop();
if (s == end) {
return dist[s];
}
int k = s.find('x');
int tmpd = dist[s];
int x = k / 3, y = k % 3;
for (int i = 0; i < 4; ++ i) {
int nx = x + dx[i], ny = y + dy[i];
if (nx >= 0 && nx < 3 && ny >= 0 && ny < 3) {
int now = nx * 3 + ny;
swap(s[k], s[now]);
if (!dist.count(s)) {
que.push(s);
dist[s] = tmpd + 1;
}
swap(s[k], s[now]);
}
}
}
return -1;
}
int main() {
string state = "";
for (int i = 0; i < 9; ++ i) {
char ch;
cin >> ch;
state += ch;
}
cout << bfs(state);
}
846. 树的重心
- ans为全局答案,是 剩余连通块中点数最大值的最小值;因此,我们每dfs到一个点假设当前这个点为重心时,size维护当前剩余连通块中点数的最大值,分别枚举这个点的儿子节点,以及儿子节点的和,用总节点数减去儿子节点的和就是父节点那边的连通块点数
- 用st数组避免枚举父节点,只枚举子节点
- dfs函数的返回值为包括当前这个点在内这个连通块的点数
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int n;
vector<int> g[N];
bool st[N];
int ans = 1e9;
int dfs(int u) {
st[u] = true;
int size = 0, sum = 0;
for (int i = 0; i < g[u].size(); ++ i) {
int j = g[u][i];
if (st[j]) continue;
st[j] = true;
int s = dfs(j);
size = max(size, s);
sum += s;
}
size = max(size, n - 1 - sum);
ans = min(ans, size);
return sum + 1;
}
int main() {
cin >> n;
for (int i = 0; i < n - 1; ++ i) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1);
cout << ans;
}
847. 图中点的层次
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int n, m;
vector<int> g[N];
int dist[N];
int bfs() {
memset(dist, -1, sizeof dist);
queue<int> que;
que.push(1);
dist[1] = 0;
while (que.size()) {
auto t = que.front();
que.pop();
if (t == n) {
return dist[t];
}
for (int i = 0; i < g[t].size(); ++ i) {
int j = g[t][i];
if (dist[j] != -1) continue;
dist[j] = dist[t] + 1;
que.push(j);
}
}
return -1;
}
int main() {
cin >> n >> m;
for (int i = 0; i < m; ++ i) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
}
cout << bfs();
}
P1219 [USACO1.5]八皇后 Checker Challenge
#include <iostream>
using namespace std;
const int N = 50;
int n;
int cnt;
int col[N], dg[N], udg[N];
int ans[N];
void dfs(int u) {
if (u == n + 1) {
cnt ++ ;
if (cnt <= 3) {
for (int i = 1; i <= n; ++ i)
cout << ans[i] << ' ';
cout << endl;
}
return ;
}
for (int i = 1; i <= n; ++ i) {
if (!col[i] && !dg[i - u + n] && !udg[i + u]) {
col[i] = dg[i - u + n] = udg[i + u] = true;
ans[u] = i;
dfs(u + 1);
// ans[u] = 0;
col[i] = dg[i - u + n] = udg[i + u] = false;
}
}
}
int main() {
cin >> n;
dfs(1);
cout << cnt;
}
P2392 kkksc03考前临时抱佛脚
- 每项作业之间是独立的,考虑一项作业,总时间是左右两边时间中的最大值,因此要总时间最少,左右两边时间要尽可能靠近所有时间的一半。
- 想到了背包,保证比t/2小,又要取到的价值尽可能大,一个物品只有两种状态,转换为一个价值和花费相等的01背包问题,背包大小为t/2
- 假设求得背包的最大价值为v,显然另一个脑耗时为t-v,总时间为max(v, t- v)
#include <iostream>
using namespace std;
const int N = 20 * 60 + 10;
int a[5], b[25];
int f[N];
int main() {
for (int _ = 1; _ <= 4; ++ _) {
cin >> a[_];
}
int ans = 0;
for (int _ = 1; _ <= 4; ++ _) {
int sum = 0;
for (int i = 1; i <= a[_]; ++ i) {
cin >> b[i];
sum += b[i];
}
for (int i = 1; i <= a[_]; ++ i) {
for (int j = sum / 2; j >= b[i]; -- j) {
f[j] = max(f[j], f[j - b[i]] + b[i]);
}
}
ans += sum - f[sum / 2];
for (int i = 0; i <= sum / 2; ++ i) {
f[i] = 0;
}
}
cout << ans;
}
- 数据范围只有20,可以搜索
#include <iostream>
using namespace std;
int a[5];
int b[5][25];
int mx, Left, Right;
void dfs(int home, int u) {
if (u > a[home]) {
mx = min(mx, max(Left, Right));
return ;
}
Left += b[home][u];
dfs(home, u + 1);
Left -= b[home][u];
Right += b[home][u];
dfs(home, u + 1);
Right -= b[home][u];
}
int main() {
for (int i = 1; i <= 4; ++ i) {
cin >> a[i];
}
for (int i = 1; i <= 4; ++ i) {
for (int j = 1; j <= a[i]; ++ j) {
cin >> b[i][j];
}
}
int ans = 0;
for (int i = 1; i <= 4; ++ i) {
mx = 1e9;
Left = Right = 0;
dfs(i, 1);
ans += mx;
}
cout << ans;
}
P1443 马的遍历
- “左对齐,宽 5 格,不能到达则输出 -1”
printf("%-5d", dist[i][j]);
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 410;
typedef pair<int, int> PII;
int n, m, x, y;
int dx[] = {-2, -2, -1, 1, 2, 2, 1, -1};
int dy[] = {-1, 1, 2, 2, 1, -1, -2, -2};
int dist[N][N];
void bfs() {
memset(dist, -1, sizeof dist);
queue<PII> que;
que.push({x, y});
dist[x][y] = 0;
while (que.size()) {
auto t = que.front();
que.pop();
for (int i = 0; i < 8; ++ i) {
int nx = t.first + dx[i], ny = t.second + dy[i];
if (nx < 1 || nx > n || ny < 1 || ny > m || dist[nx][ny] != -1) continue;
que.push({nx, ny});
dist[nx][ny] = dist[t.first][t.second] + 1;
}
}
}
int main() {
cin >> n >> m >> x >> y;
bfs();
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
printf("%-5d", dist[i][j]);
}
cout << endl;
}
}
P1135 奇怪的电梯
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 210;
int n, x, y;
int a[N];
bool st[N];
int dist[N];
int bfs() {
queue<int> que;
memset(dist, -1, sizeof dist);
que.push(x);
dist[x] = 0;
while (que.size()) {
auto t = que.front();
que.pop();
if (t == y) {
return dist[t];
}
int up = t + a[t];
if (up >= 1 && up <= n && dist[up] == -1) {
que.push(up);
dist[up] = dist[t] + 1;
}
int down = t - a[t];
if (down >= 1 && down <= n && dist[down] == -1) {
que.push(down);
dist[down] = dist[t] + 1;
}
}
return -1;
}
int main() {
cin >> n >> x >> y;
for (int i = 1; i <= n; ++ i) {
cin >> a[i];
}
cout << bfs();
}
P2895 [USACO08FEB]Meteor Shower S
- 首先根据题目所给的所有陨石,将所有会被砸到的陨石标记,地图中所有点代表这个点被陨石砸到的时间,如果不会被砸到设置为1010(大于时间上限)
- 最短时间,就是bfs,只要搜到一个点砸到时间为1010,说明这个点是安全的,退出bfs
- 更新队列时,不仅不能出第一象限,不能被走过,还需要这个点在这个时间之前没有被砸过
- 注意这道题可以走到300以外
- 注意陨石下落时间以最早的为准!!!
#include <iostream>
#include <queue>
using namespace std;
const int N = 510;
typedef pair<int, int> PII;
int n;
int g[N][N];
bool st[N][N];
int dist[N][N];
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
int bfs() {
queue<PII> que;
que.push({0, 0});
st[0][0] = true;
dist[0][0] = 0;
while (que.size()) {
auto t = que.front();
que.pop();
int x = t.first, y = t.second;
if (g[x][y] == 1010) {
return dist[x][y];
}
for (int i = 0; i < 4; ++ i) {
int nx = x + dx[i], ny = y + dy[i];
if (nx >= 0 && ny >= 0 && !st[nx][ny] && (g[nx][ny] > (dist[x][y] + 1))) {
que.push({nx, ny});
st[nx][ny] = true;
dist[nx][ny] = dist[x][y] + 1;
}
}
}
return -1;
}
int main() {
cin >> n;
for (int i = 0; i < N; ++ i) {
for (int j = 0; j < N; ++ j) {
g[i][j] = 1010;
}
}
for (int i = 0; i < n; ++ i) {
int x, y, t;
cin >> x >> y >> t;
g[x][y] = min(g[x][y], t);
for (int j = 0; j < 4; ++ j) {
int nx = x + dx[j], ny = y + dy[j];
if (nx >= 0 && ny >= 0) {
g[nx][ny] = min(g[nx][ny], t);
}
}
}
cout << bfs();
}
P1036 [NOIP2002 普及组] 选数
#include <iostream>
using namespace std;
const int N = 25;
int n, k;
int a[N];
int ans;
bool check(int x) {
if (x == 1) return false;
for (int i = 2; i * i <= x; ++ i) {
if (x % i == 0) {
return false;
}
}
return true;
}
void dfs(int u, int sum, int now) {
if (u == n + 1) {
if (now == k && check(sum)) {
ans ++ ;
}
return ;
}
dfs(u + 1, sum + a[u], now + 1);
dfs(u + 1, sum, now);
}
int main() {
cin >> n >> k;
for (int i = 1; i <= n; ++ i) {
cin >> a[i];
}
dfs(1, 0, 0);
cout << ans;
}
P2036 [COCI2008-2009#2] PERKET
#include <iostream>
using namespace std;
const int N = 15;
int n;
int a[N], b[N];
int ans = 2e9 + 1;
void dfs(int u, int suan, int ku, int cnt) {
if (u == n + 1) {
if (cnt) {
ans = min(ans, abs(suan - ku));
}
return ;
}
dfs(u + 1, suan * a[u], ku + b[u], cnt + 1);
dfs(u + 1, suan, ku, cnt);
}
int main() {
cin >> n;
for (int i = 1; i <= n; ++ i) {
cin >> a[i] >> b[i];
}
dfs(1, 1, 0, 0);
cout << ans;
}
P1433 吃奶酪(状压dp)
1.搜索
简单介绍一下搜索的技巧之一:卡时。
什么时候可以用到卡时呢?
当求最优解(max,min)(max,min)时,数据范围较大(但是如果过大那就别想了),我们可以贪心的卡时一下。
总的来说,卡时就是不断的试我们认为的最优解,次优解,最终找出真正的最优解。
就这道题来说,我们可以贪心的每次找离目前点最近的点,次近的点,以达到找到的解中在规定时限内出现最优解的情况。
看到这里可能会很笼统,好吧,其实就是在朴素的搜索里加一个特判,如果我们发现即将要超时,那么直接输出我们找到过的最优解
3e7和940
记录每条边的终点和长度
#include <iostream>
#include <cmath>
#include <algorithm>
#include <ctime>
using namespace std;
const int N = 17;
struct Node {
double dist;
int ed;
bool operator< (const Node &w) const {
return dist < w.dist;
}
};
int n;
double x[N], y[N];
Node a[N][N];
bool st[N];
double ans = 1e10;
int l;
void dfs(int u, double sum, int cnt) {
l ++ ;
if (l >= 30000000) {
int t = clock();
if (t >= 940) {
printf("%.2lf", ans);
exit(0);
}
}
if (sum >= ans) return ;
if (cnt == n) {
ans = min(ans, sum);
return ;
}
for (int i = 1; i <= n; ++ i) {
if (st[a[u][i].ed] || u == a[u][i].ed) continue;
st[a[u][i].ed] = true;
dfs(a[u][i].ed, sum + a[u][i].dist, cnt + 1);
st[a[u][i].ed] = false;
}
}
int main() {
cin >> n;
n ++ ;
x[1] = 0, y[1] = 0;
for (int i = 2; i <= n; ++ i) {
cin >> x[i] >> y[i];
}
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
a[i][j].dist = sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j])));
a[i][j].ed = j;
}
sort(a[i] + 1, a[i] + n + 1);
}
st[1] = true;
dfs(1, 0, 1);
printf("%.2lf", ans);
}
2.状压dp
设f[i][s]表示从i点出发遍历集合为s的点的路程最小值(i也包括在s里),枚举s里的其他点进行转移。
边界为f[i][s]=0(s中只有i)。
注意最后答案要加上到(0,0)的距离。
时间复杂度O(n*2^n)
memset 127 表示int上限,double数组也可以这样赋值
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 17;
int n;
double x[N], y[N];
double f[N][1 << 18];
double calc(int a, int b) {
return sqrt((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b]));
}
int main() {
cin >> n;
for (int i = 0; i < n; ++ i) {
cin >> x[i] >> y[i];
}
memset(f, 127, sizeof f);
for (int s = 0; s < (1 << n); ++ s) {
for (int i = 0; i < n; ++ i) {
if (!(s >> i & 1)) continue;
if (s == (1 << i)) {
f[i][s] = 0;
continue;
}
for (int j = 0; j < n; ++ j) {
if (i == j) continue;
if (!(s >> j & 1)) continue;
f[i][s] = min(f[i][s], f[j][s - (1 << i)] + calc(i, j));
}
}
}
double ans = -1;
for (int i = 0; i < n; ++ i) {
double now = f[i][(1 << n) - 1] + sqrt(x[i] * x[i] + y[i] * y[i]);
if (ans == -1 || now < ans) {
ans = now;
}
}
printf("%.2lf", ans);
}
P1605 迷宫
#include <iostream>
using namespace std;
const int N = 10;
int n, m, t;
int g[N][N];
int sx, sy, ex, ey;
int ans;
bool st[N][N];
int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
void dfs(int x, int y) {
if (x == ex && y == ey) {
ans ++ ;
return ;
}
for (int i = 0; i < 4; ++ i) {
int nx = x + dx[i], ny = y + dy[i];
if (nx < 1 || nx > n || ny < 1 || ny > m || st[nx][ny] || g[nx][ny]) continue;
st[nx][ny] = 1;
dfs(nx, ny);
st[nx][ny] = 0;
}
}
int main() {
cin >> n >> m >> t;
cin >> sx >> sy >> ex >> ey;
for (int i = 0; i < t; ++ i) {
int x, y;
cin >> x >> y;
g[x][y] = 1;
}
st[sx][sy] = 1;
dfs(sx, sy);
cout << ans;
}
P1019 [NOIP2000 提高组] 单词接龙
两个单词合并时,合并部分取的是最小重叠部分
相邻的两部分不能存在包含关系就是说如果存在包含关系,就不能标记为使用过。
每个单词最多出现两次.
首先是预处理,用coin[i][j]来存储 第i个单词 后连接 第j个单词 的 最小重叠部分
#include <iostream>
using namespace std;
const int N = 25;
int n;
string tr[N];
int st[N]; // 字符串被使用的次数
int coin[N][N]; // 第一个字符串后面接第二个字符串的最小重叠部分
int ans, now;
int calc_coin(int a, int b) {
bool ok = true;
for (int k = (int)tr[a].size() - 1; k >= 0; -- k) {
ok = true;
for (int i = k, j = 0; i < tr[a].size(); ++ i, ++ j) {
if (j >= tr[b].size() || tr[a][i] != tr[b][j]) {
ok = false;
break;
}
}
if (ok) {
return (int)tr[a].size() - 1 - k + 1;
}
}
return 0;
}
void dfs(int i) {
ans = max(now, ans);
for (int j = 1; j <= n; ++ j) {
if (st[j] >= 2) continue;
if (coin[i][j] == 0) continue;
if (coin[i][j] == tr[i].size() || coin[i][j] == tr[j].size()) continue;
st[j] ++ ;
now += (tr[j].size() - coin[i][j]);
dfs(j);
st[j] -- ;
now -= (tr[j].size() - coin[i][j]);
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; ++ i) {
cin >> tr[i];
}
char ch;
cin >> ch;
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
coin[i][j] = calc_coin(i, j);
}
}
for (int i = 1; i <= n; ++ i) {
if (tr[i][0] == ch) {
now = (int)tr[i].size();
st[i] ++ ;
dfs(i);
st[i] -- ;
}
}
cout << ans;
}
P1101 单词方阵
#include <iostream>
using namespace std;
const int N = 110;
int n;
char g[N][N];
bool st[N][N];
string ss = "yizhong";
int dx[] = {-1, -1, -1, 0, 1, 1, 1, 0};
int dy[] = {-1, 0, 1, 1, 1, 0, -1, -1};
bool dfs(int x, int y, int u, int k) {
if (u == 8) return true;
if (g[x][y] != ss[u - 1]) return false;
bool t = dfs(x + dx[k], y + dy[k], u + 1, k);
st[x][y] |= t;
return t;
}
int main() {
cin >> n;
for (int i = 0; i < n; ++ i) {
cin >> g[i];
}
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
if (g[i][j] == 'y') {
for (int k = 0; k < 8; ++ k) {
dfs(i, j, 1, k);
}
}
}
}
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
if (st[i][j]) {
cout << g[i][j];
} else {
cout << '*';
}
}
cout << endl;
}
}
P2404 自然数的拆分问题
#include <iostream>
#include <vector>
using namespace std;
const int N = 10;
int n;
int a[N];
void dfs(int u, int last, int sum) {
if (sum > n) return ;
if (sum == n) {
for (int i = 1; i < u; ++ i) {
cout << a[i];
if (i != u - 1) cout << "+";
}
cout << endl;
return ;
}
for (int i = last; i < n; ++ i) {
a[u] = i;
dfs(u + 1, i, sum + i);
}
}
int main() {
cin >> n;
dfs(1, 1, 0);
}
P1596 [USACO10OCT]Lake Counting S
#include <iostream>
using namespace std;
const int N = 110;
int n, m;
char g[N][N];
bool st[N][N];
int dx[] = {-1, -1, -1, 0, 1, 1, 1, 0};
int dy[] = {-1, 0, 1, 1, 1, 0, -1, -1};
void dfs(int x, int y) {
st[x][y] = true;
for (int i = 0; i < 8; ++ i) {
int nx = x + dx[i], ny = y + dy[i];
if (nx < 0 || nx >= n || ny < 0 || ny >= m || st[nx][ny] || g[nx][ny] == '.') continue;
dfs(nx, ny);
}
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; ++ i) {
cin >> g[i];
}
int ans = 0;
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
if (g[i][j] == 'W' && !st[i][j]) {
ans ++ ;
dfs(i, j);
}
}
}
cout << ans;
}
P1162 填涂颜色
第一想法是dfs后走不到的地方就是1或者2;但是注意那些被1围堵在角落的0,它们不是被1的闭合圈包围的,因此仍然是0,
一个好的想法便是在周围加一圈0,且从(0,0)开始搜索。这样的话,被堵在墙角的0可以通过旁边加上的0被dfs到
为什么必须从(0,0)开始搜索呢?因为如果(1,1)本身就是墙,且处于闭合圈的话,闭合圈内的0也会被dfs;而(0,0)是被加上的一圈0,不会出现这个问题
#include <iostream>
using namespace std;
const int N = 35;
int n;
int g[N][N];
bool st[N][N];
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};
void dfs(int x, int y) {
st[x][y] = true;
for (int i = 0; i < 4; ++ i) {
int nx = x + dx[i], ny = y + dy[i];
// if (nx < 1 || nx > n || ny < 1 || ny > n || st[nx][ny] || g[nx][ny] == 1) continue;
if (nx < 0 || nx > n + 1 || ny < 0 || ny > n + 1 || st[nx][ny] || g[nx][ny] == 1) continue;
dfs(nx, ny);
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
cin >> g[i][j];
}
}
// dfs(1, 1);
dfs(0, 0);
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
if (g[i][j]) {
cout << 1;
} else if (st[i][j]) {
cout << 0;
} else {
cout << 2;
}
cout << ' ';
}
cout << endl;
}
}