问题 A: 【递归入门】全排列
时间限制: 1 Sec 内存限制: 128 MB
提交: 661 解决: 414
[提交][状态][讨论版][命题人:外部导入]
题目描述
排列与组合是常用的数学方法。
先给一个正整数 ( 1 < = n < = 10 )
例如n=3,所有组合,并且按字典序输出:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
输入
输入一个整数n( 1<=n<=10)
输出
输出所有全排列
每个全排列一行,相邻两个数用空格隔开(最后一个数后面没有空格)
样例输入
3
样例输出
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
方法1.投机取巧next_permutation()
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int n;
int a[11];
while(cin>>n){
for(int i=0;i<n;i++) a[i]=i+1;
do{
for(int i=0;i<n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}while(next_permutation(a,a+n));
}
return 0;
}
方法二:老老实实dfs写递归
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int a[11];
int n;
bool Hash[11];
//index当前排第index位的数 也即是当前排的数在最终结果中的第index位
void dfs(int index){
if(index==n){
for(int i=0;i<n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}
for(int i=1;i<=n;i++){
if(Hash[i]==false){//当前数字没有被排过(本条路径中)
//i排到index位
Hash[i]=true;
a[index]=i;
dfs(index+1);
Hash[i]=false;//不排当前数字 排下一个数字(也即是下一次循环的dfs)
}
}
}
int main(){
int a[11];
while(cin>>n){
memset(Hash,0,sizeof(Hash));
for(int i=0;i<n;i++) a[i]=i+1;
dfs(0);
}
return 0;
}
问题 B: 【递归入门】组合的输出
时间限制: 1 Sec 内存限制: 128 MB
提交: 442 解决: 266
[提交][状态][讨论版][命题人:外部导入]
题目描述
排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r < = n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。
现要求你不用递归的方法输出所有组合。
例如n = 5 ,r = 3 ,所有组合为:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
输入
一行两个自然数n、r ( 1 < n < 21,1 < = r < = n )。
输出
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,所有的组合也按字典顺序。
方法1:在一全排列的基础上修改,到r时输出,且下次只排比本次大的数
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int a[30];
int n,r;
bool Hash[30];
//index当前排第index位的数 也即是当前排的数在最终结果中的第index位
void dfs(int index){
if(index==r){
for(int i=0;i<r;i++){
cout<<a[i]<<" ";
}
cout<<endl;
return;//前面不写return没事 都是false 慢一点而已 此处必须写return
}
for(int i=1;i<=n;i++){
if(index>0&&i<a[index-1]) continue;//小于已经排好的不排
if(Hash[i]==false){//当前数字没有被排过(本条路径中)
//i排到index位
Hash[i]=true;
a[index]=i;
dfs(index+1);
Hash[i]=false;//不排当前数字 排下一个数字(也即是下一次循环的dfs)
}
}
}
int main(){
while(cin>>n>>r){
memset(Hash,0,sizeof(Hash));
dfs(0);
}
return 0;
}
方法二:简单递归
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int a[30];
int n,r;
//index当前排第index位的数 也即是当前排的数在最终结果中的第index位
void dfs(int index){
if(index==r){
for(int i=0;i<r;i++){
cout<<a[i]<<" ";
}
cout<<endl;
return;//前面不写return没事 都是false 慢一点而已 此处必须写return
}
for(int i=1;i<=n;i++){
if(index==0||i>a[index-1]){//当前数字没有被排过(本条路径中)
//i排到index位
a[index]=i;
dfs(index+1);
}
}
}
int main(){
int a[11];
while(cin>>n>>r){
dfs(0);
}
return 0;
}
方法三:典型的m个数中选n个的问题
#include<iostream>
#include<vector>
using namespace std;
int n,k;
vector<int> vi;
//典型的m个数中选n个的问题
void dfs(int index,int nowK){
if(nowK==k){ //vi.size()一定等于r
for(int i=0;i<vi.size();i++){
cout<<vi[i]<<" ";
}
cout<<endl;
return;
}
if(index==n) return;
//选第index个
vi.push_back(index+1);//第index个的值就是index+1 懒得开数组存了
dfs(index+1,nowK+1);
vi.pop_back();
//不选第index个
dfs(index+1,nowK);//总是忘记 第index个不排,就去排第index+1个呀 +1别忘了 ★★★
}
int main(){
while(cin>>n>>k){
vi.clear();
dfs(0,0);
}
return 0;
}
方法四:一位大佬巧用pre_permutation()
/*
STL algorithm中prev_permutation函数:
a存储总排列,b存储数字。例如 5 3:
则a数组开始为11100,b数组为12345
开始输出123,a数组前一个排列为11010,输出124
以此类推直到00111输出345结束
*/
#include <cstdio>
#include <algorithm>
using namespace std;
int main(){
int n, m;
scanf("%d %d", &n, &m);
int a[25] = { 0 }, b[25] = { 0 };
for (int i = 0; i < m; i++)
a[i] = 1;
for (int i = 0; i < n; i++)
b[i] = i + 1;
for (int i = 0; i < m; i++) {
printf("%d ", b[i]);
}
printf("\n");
while (prev_permutation(a, a + n)){
for (int i = 0; i < n; i++)
if (a[i])
printf("%d ", b[i]);
printf("\n");
}
}
问题 C: 【递归入门】组合+判断素数
时间限制: 1 Sec 内存限制: 128 MB
提交: 828 解决: 246
[提交][状态][讨论版][命题人:外部导入]
题目描述
已知 n 个整数b1,b2,…,bn
以及一个整数 k(k<n)。
从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。
例如当 n=4,k=3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:
3+7+12=22 3+7+19=29 7+12+19=38 3+12+19=34。
现在,要求你计算出和为素数共有多少种。
例如上例,只有一种的和为素数:3+7+19=29。
输入
第一行两个整数:n , k (1<=n<=20,k<n)
第二行n个整数:x1,x2,…,xn (1<=xi<=5000000)
输出
一个整数(满足条件的方案数)。
样例输入
4 3
3 7 12 19
样例输出
1
判断素数用最常规的做法,用欧式筛法因为基数范围太大反而会超时
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
int n,k;
vector<int> vi;
int a[25];
int Count;
bool isPrime(long long n){
for(int i=2;i<=sqrt(n);i++){
if(n%i==0) return false;
}
return true;
}
//典型的m个数中选n个的问题
void dfs(int index,int nowK){
if(nowK==k){ //vi.size()一定等于r
int sum=0;
for(int i=0;i<vi.size();i++){
sum+=vi[i];
}
if(isPrime(sum)){
Count++;
}
return;
}
if(index==n) return;
//选第index个
vi.push_back(a[index]);//第index个的值就是index+1 懒得开数组存了
dfs(index+1,nowK+1);
vi.pop_back();
//不选第index个
dfs(index+1,nowK);//总是忘记 第index个不排,就去排第index+1个呀 +1别忘了 ★★★
}
int main(){
// FindPrime();
while(cin>>n>>k){
vi.clear();
Count=0;
for(int i=0;i<n;i++){
cin>>a[i];
}
dfs(0,0);
cout<<Count<<endl;
}
return 0;
}
方法二:计算sum也放到递归里,代码简洁好看一点
#include<iostream>
#include <cmath>
using namespace std;
typedef long long ll;
int n,k;
int a[25];
int Count;
bool isprime(ll sum){
for(int i=2;i<=sqrt(sum);i++){
if(sum%i==0) return false;
}
return true;
}
//选到第index个 共选了nowK个 当前和sum
void dfs(int index,int nowK,ll sum){
if(nowK==k){
if(isprime(sum)) {
Count++;
}
return;
}
if(index==n) return;//index==n时停止 不是>n 不是自己的代码始终学不会。。
dfs(index+1,nowK+1,sum+a[index]);
dfs(index+1,nowK,sum);
}
int main(){
while(cin>>n>>k){
Count=0;
for(int i=0;i<n;i++){
cin>>a[i];
}
dfs(0,0,0);
cout<<Count<<endl;
}
return 0;
}
问题 D: 【递归入门】n皇后 问题(原始的8皇后问题)
时间限制: 1 Sec 内存限制: 128 MB
提交: 364 解决: 177
[提交][状态][讨论版][命题人:外部导入]
题目描述
会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。
输入
一个整数n( 1 < = n < = 10 )
输出
每行输出对应一种方案,按字典序输出所有方案。每种方案顺序输出皇后所在的列号,相邻两数之间用空格隔开。如果一组可行方案都没有,输出“no solute!”
样例输入
4
样例输出
2 4 1 3
3 1 4 2
借助next_permutation能解决不少问题
#include<iostream>
#include <algorithm>
using namespace std;
int a[11];
int main(){
int n;
while(cin>>n){
if(n<4){
cout<<"no solute!\n";
continue;
}
for(int i=0;i<n;i++){
a[i]=i+1;
}
do{
bool flag=true;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(abs(i-j)==abs(a[i]-a[j])){
//排列保证不同行不同列
//斜率保证不同对角线
flag=false;
}
}
}
if(flag){
for(int i=0;i<n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}
}while(next_permutation(a,a+n));
}
return 0;
}
问题 E: 【递归入门】出栈序列统计
时间限制: 1 Sec 内存限制: 128 MB
提交: 198 解决: 147
[提交][状态][讨论版][命题人:外部导入]
题目描述
栈是常用的一种数据结构,有n令元素在栈顶端一侧等待进栈,栈顶端另一侧是出栈序列。你已经知道栈的操作有两•种:push和pop,前者是将一个元素进栈,后者是将栈顶元素弹出。现在要使用这两种操作,由一个操作序列可以得到一系列的输出序列。请你编程求出对于给定的n,计算并输出由操作数序列1,2,…,n,经过一系列操作可能得到的输出序列总数。
输入
一个整数n(1<=n<=15)
输出
一个整数,即可能输出序列的总数目。
样例输入
3
样例输出
5
提示
先了解栈的两种基本操作,进栈push就是将元素放入栈顶,栈顶指针上移一位,等待进栈队列也上移一位,出栈pop是将栈顶元素弹出,同时栈顶指针下移一位。
用一个过程采模拟进出栈的过程,可以通过循环加递归来实现回溯:重复这样的过程,如果可以进栈则进一个元素,如果可以出栈则出一个元素。就这样一个一个地试探下去,当出栈元素个数达到n时就计数一次(这也是递归调用结束的条件)。
#include<iostream>
using namespace std;
int num,n;
void dfs(int in,int out){
if(out>in||out>n||in>n) return;
if(out==n){
num++;
return;
}
dfs(in+1,out);
dfs(in,out+1);
}
int main(){
while(cin>>n){
num=0;
dfs(0,0);
cout<<num<<endl;
}
return 0;
}
问题 F: 【递归入门】走迷宫
时间限制: 1 Sec 内存限制: 128 MB
提交: 480 解决: 159
[提交][状态][讨论版][命题人:外部导入]
题目描述
有一个n*m格的迷宫(表示有n行、m列),其中有可走的也有不可走的,如果用1表示可以走,0表示不可以走,文件读入这n*m个数据和起始点、结束点(起始点和结束点都是用两个数据来描述的,分别表示这个点的行号和列号)。现在要你编程找出所有可行的道路,要求所走的路中没有重复的点,走时只能是上下左右四个方向。如果一条路都不可行,则输出相应信息(用-l表示无路)。
请统一用 左上右下的顺序拓展,也就是 (0,-1),(-1,0),(0,1),(1,0)
输入
第一行是两个数n,m( 1 < n , m < 15 ),接下来是m行n列由1和0组成的数据,最后两行是起始点和结束点。
输出
所有可行的路径,描述一个点时用(x,y)的形式,除开始点外,其他的都要用“->”表示方向。
如果没有一条可行的路则输出-1。
样例输入
5 6 1 0 0 1 0 1 1 1 1 1 1 1 0 0 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 1 1 1 5 6
样例输出
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(3,4)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(3,4)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6) (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6)
提示
【算法分析】
用一个a数组来存放迷宫可走的情况,另外用一个数组b来存放哪些点走过了。每个点用两个数字来描述,一个表示行号,另一个表示列号。对于某一个点(x,y),四个可能走的方向的点描述如下表:
2
1 x,y 3
4
对应的位置为:(x, y-1),(x-1, y),(x, y+1),(x+1, y)。所以每个点都要试探四个方向,如果没有走过(数组b相应的点的值为0)且可以走(数组a相应点的值为1)同时不越界,就走过去,再看有没有到达终点,到了终点则输出所走的路,否则继续走下去。
这个查找过程用search来描述如下:
procedure search(x, y, b, p);{x,y表示某一个点,b是已经过的点的情况,p是已走过的路}
begin
for i:=1 to 4 do{分别对4个点进行试探}
begin
先记住当前点的位置,已走过的情况和走过的路;
如果第i个点(xl,y1)可以走,则走过去;
如果已达终点,则输出所走的路径并置有路可走的信息,
否则继续从新的点往下查找search(xl,y1,b1,p1);
end;
end;
有些情况很明显是无解的,如从起点到终点的矩形中有一行或一列都是为0的,明显道路不通,对于这种情况要很快地“剪掉”多余分枝得出结论,这就是搜索里所说的“剪枝”。从起点开始往下的一层层的结点,看起来如同树枝一样,对于其中的“枯枝”——明显无用的节点可以先行“剪掉”,从而提高搜索速度。
注意:搜索顺序请统一用 左上右下的顺序拓展,也就是 (0,-1),(-1,0),(0,1),(1,0) 否则答案顺序与测试数据顺序不一致
dfs为了使每次互不影响,每次dfs有几个push就要有几个pop
开始dfs之前将起点置为0或者2(非1)。此点必走过,大胆置0(或2 非1)
Count记录路径总数
代码1:
#include<iostream> #include <utility> #include <vector> #include <cstdio> using namespace std; const int N = 20; int a[N][N] = { 0 }; int b[N][N] = { 0 }; int m, n, xs, ys, xe, ye;//m行n列矩阵 起点(xs,ys) 终点(xe,ye) int dx[4] = { 0,-1,0,1 };//最后一个1写成了-1 结果卡了好久 输出关键值来debug int dy[4] = { -1,0,1,0 }; vector<pair<int, int> > ans; int Count; void dfs(int x, int y) { ans.push_back(make_pair(x, y));//能走过来 都是合格的 立刻入队 if (x == xe&&y == ye) { Count++;//顺便记录路径总数 for (int i = 0;i<ans.size();i++) { if (i != 0) printf("->"); printf("(%d,%d)", ans[i].first, ans[i].second); } printf("\n"); return; } for (int i = 0;i<4;i++) { int nx = x + dx[i]; int ny = y + dy[i]; if (b[nx][ny]) { b[nx][ny] = 0; // ans.push_back(make_pair(nx,ny));//此处入队不合适 dfs(nx, ny); //关键两步 使得dfs每条路径互不干扰成为可能 ans.pop_back();//异常关键 每次pop()一次 一步一步退到第一次到此的情形 一次push对应一次pop 不会出错 b[nx][ny] = 1;//很关键 } } } int main() { // freopen("f.txt", "r", stdin); while (scanf("%d%d",&m,&n)!=EOF) { for (int i = 1;i <= m;i++) { for (int j = 1;j <= n;j++) { // cin >> a[i][j]; scanf("%d",&a[i][j]); b[i][j] = a[i][j]; } } // cin >> xs >> ys >> xe >> ye; scanf("%d%d%d%d",&xs,&ys,&xe,&ye); ans.clear(); Count = 0; b[xs][ys]=0;//走过了 开始第一步走的就是这个点 置为0 很重要★★ dfs(xs, ys);//起点开始 不是0 0 if (Count == 0) printf("-1\n"); } return 0; }
代码2
#include<iostream> #include <utility> #include <vector> using namespace std; const int N=20; pair<int,int> Start,End; int n,m,Count; //请统一用 左上右下的顺序拓展,也就是 (0,-1),(-1,0),(0,1),(1,0) int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0}; vector<pair<int,int> > ans;//路径结果 int a[N][N]={0}; void dfs(int x,int y){ ans.push_back(make_pair(x,y)); if(x==End.first&&y==End.second){ Count++; for(int i=0;i<ans.size();i++){ if(i!=0) cout<<"->"; cout<<"("<<ans[i].first<<","<<ans[i].second<<")"; } cout<<endl; return;//重要的递归边界可不能忘了 } for(int i=0;i<4;i++){ int next_x=x+dx[i]; int next_y=y+dy[i]; if(a[next_x][next_y]==1){//未走过 a[next_x][next_y]=2;//走过 dfs(next_x,next_y); //走完还原后 再尝试别的方向 ans.pop_back(); a[next_x][next_y]=1;//还原 } } } int main(){ //int n,m; //千万不要重复定义全局变量 while(cin>>n>>m){ //题目要求下标(1,1)开始 for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>a[i][j]; } } cin>>Start.first>>Start.second>>End.first>>End.second; ans.clear(); Count=0; a[Start.first][Start.second]=2;//此步必走 0不可走 1可走 2走过 dfs(Start.first,Start.second); if(Count==0) cout<<"-1\n"; } return 0; }
代码3:进一步在迷宫总标出路径
#include<iostream> #include <utility> #include <vector> using namespace std; const int N=20; pair<int,int> Start,End; int n,m,Count; //请统一用 左上右下的顺序拓展,也就是 (0,-1),(-1,0),(0,1),(1,0) int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0}; vector<pair<int,int> > ans;//路径结果 int a[N][N]={0}; void Print(int a[N][N]){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cout<<a[i][j]<<" "; } cout<<endl; } } void dfs(int x,int y){ ans.push_back(make_pair(x,y)); if(x==End.first&&y==End.second){ Count++; for(int i=0;i<ans.size();i++){ if(i!=0) cout<<"->"; cout<<"("<<ans[i].first<<","<<ans[i].second<<")"; } cout<<endl; cout<<"path:\n"; Print(a); cout<<endl; return;//重要的递归边界可不能忘了 } for(int i=0;i<4;i++){ int next_x=x+dx[i]; int next_y=y+dy[i]; if(a[next_x][next_y]==1){//未走过 a[next_x][next_y]=2;//走过 dfs(next_x,next_y); //走完还原后 再尝试别的方向 ans.pop_back(); a[next_x][next_y]=1;//还原 } } } int main(){ // int n,m;该死 重复定义n,m while(cin>>n>>m){ //题目要求下标(1,1)开始 for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>a[i][j]; } } cin>>Start.first>>Start.second>>End.first>>End.second; ans.clear(); Count=0; a[Start.first][Start.second]=2;//此步必走 0不可走 1可走 2走过 dfs(Start.first,Start.second); if(Count==0) cout<<"-1\n"; } return 0; }
输出结果:
输入: 5 6 1 0 0 1 0 1 1 1 1 1 1 1 0 0 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 1 1 1 5 6 输出: (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(3,4)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 2 2 1 0 0 2 2 2 0 1 1 2 2 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 2 2 1 0 0 1 2 2 0 1 1 1 2 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 2 2 1 0 0 1 1 2 0 1 1 1 1 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 2 1 1 0 0 2 2 1 0 1 1 2 2 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 2 1 1 0 0 1 2 2 0 1 1 1 1 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 2 1 1 0 0 1 2 1 0 1 1 1 2 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 2 2 1 0 0 2 2 2 0 1 1 1 1 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 1 1 1 0 0 2 2 2 0 1 1 1 1 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 1 1 1 0 0 2 2 1 0 1 1 1 2 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(3,4)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 2 2 1 0 0 2 2 2 0 1 1 2 2 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 1 1 1 0 0 2 2 2 0 1 1 2 2 2 0 1 1 1 0 2 2 (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6) path: 2 0 0 1 0 1 2 2 2 1 1 1 0 0 2 1 1 0 1 1 2 2 2 0 1 1 1 0 2 2