题目一:
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;
}