回溯法解题报告

题目一:

HDOJ 1016

原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=1016

问题描述

如图所示,环由n个圆组成。将自然数1,2,…,n分别放入每个圆圈中,并且相邻两个圆圈中的数字总和应为素数。

注意:第一个圆圈的数量应该始终为1。
 

输入

n(0 <n <20)。

 

输出

输出格式如下所示。每行代表从1开始顺时针和逆时针旋转的一系列圆圈数字。数字的顺序必须符合上述要求。按照字典顺序打印解决方案。

你要编写一个完成上述过程的程序。

在每个案例后打印一个空行。

 

示例输入

6

8

 

示例输出

Case 1:

1 4 3 2 5 6

1 6 5 2 3 4

 

Case 2:

1 2 3 8 5 6 7 4

1 2 5 8 3 4 7 6

1 4 7 6 5 8 3 2

1 6 7 4 3 8 5 2

 

解题思路:

因为输入的范围是(0,20)并且相邻的数必不重复,所以相邻两个数相加最大为37,因此可以先找出(2,38)内的所有素数,建一个数组,对应的位置是素数时标记为1。因为往圆圈中填数时是按照顺时针或者逆时针方向,所以只要保证当前填入的数和上一个数之和为素数,并且填到第n个数时保证第n个数和第一个数(1)的和也是素数,那么得到的这个解就是正确的。

 

 

 

程序:

#include<iostream>

#include<vector>

usingnamespace std;

 

constint n = 40;

int N, x = 0;

int num[n]; //保存(1,40)内所有的素数

int Find[n]; //记录(1, 20)内的数是否出现过

vector<int> a;

 

bool findNum(intk) //查找该数是否出现过

{

    if(Find[k] == 0)

       returntrue;

    else

       returnfalse;

}

 

bool Task(intk) //判断与前一个数相加是否为素数

{

    if(!a.empty())

       k += a[a.size()-1];

    if(num[k] == 1)

       returntrue;

    else

       returnfalse;

}

 

void PlaceNum(intk)

{

    if(k > N)

    {

       if(num[a[a.size()-1]+1] == 1)

       {

           int i;

           for(i =a.size() – N; i < a.size() – 1; i++)

              cout << a[i]<<‘ ‘;

           cout << a[i]<< endl;

       }

    }

    else

    {

       for(int i = 2; i <= N; i++)

       {

           if(findNum(i)&& Task(i))

           {

              Find[i] = 1;

              a.push_back(i);

              PlaceNum(k +1);

              Find[i] = 0;

              a.pop_back();

           }

       }

    }

}

 

int main()

{

    for(int i = 2; i < n; i++) //找出(1, 40)内所有的素数

    {

       int s = 1;

       for(int j = 2; j < i; j++)

       {

           if(i %j == 0)

           {

              s = 0;

              break;

           }

       }

       if(s == 1)

           num[i] = 1;

    }

    while(cin >> N)

    {

       cout <<“Case “<< ++x <<“:”<< endl;

       a.push_back(1);

       Find[1] = 1;

       PlaceNum(2);

       cout <<endl;

    }

    return 0;

}

 

 

 

题目二:

HDOJ 2563

原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=2563

问题描述

在一无限大的二维平面中,我们做如下假设:
1,每次只能移动一格; 
2,不能向后走(假设你的目的地是“向上”,那么你可以向左走,可以向走走,也可以向上走,但是不可以向下走); 
3,走过的格子立即塌陷无法再走第二次; 

求走n步不同的方案数(2种走法只要有一步不一样,即被认为是不同的方案)。

 

输入

首先给出一个正整数C,表示有C组测试数据
接下来的C行,每行包含一个整数n(n <= 20),表示要走n步。

 

输出

请编程输出走Ñ步的不同方案总数; 
每组的输出占一行。

 

示例输入

2

1

2

 

示例输出

3

7

 

解题思路:

回溯法思路(当n比较大时会超时):

因为输入的数据是从(0,20],所以可以开一个比较大的数组来记录某个位置是否走过,如果走过则标记为1,没走过时为0。这时可以把移动分为3种情况,第一种为向前移动(数组行数+1),第二种为向左移动(数组列数-1),第三种为向右移动(数组列数+1),移动的次数大于n时则结束。

递推思路:

当移动第n步时有两种情况,一种是可移动的位置有3个(当第n-1步为向上移动时),另外一种是可移动的位置有2个(当第n-1步为向左或向右移动时)。可移动的位置有2个的所有可能的情况的个数等于移动第n-1步所有可能的情况的个数乘以2,可移动的位置有3个的所有可能的情况的个数等于第n-1步为向上移动的情况的个数,而第n-1步为向上移动的情况的个数又等于第n-2步的所有可能的情况的个数,所以递推的公式为:

Count[n] = 2 * Count[n-1] + Count[n-2]

(Count数组用来存放移动第n步所有可能的情况的个数,初始化第0步为1,第1步为3)

 

 

 

程序:

#include<iostream>

usingnamespace std;

 

int main()

{

    int a[21], N, n;

    a[0]= 1;

    a[1]= 3;

    for(int i = 2; i <= 20;i++)

       a[i]= 2 * a[i – 1] + a[i – 2];

    cin>> N;

    while(N–)

    {

       cin>> n;

       cout<< a[n] << endl;

    }

    return 0;

}

 

    原文作者:回溯法
    原文地址: https://blog.csdn.net/jq1491063607/article/details/80641197
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞