1.半数集问题
问题描述:
给定一个自然数n,由n开始可以依次产生半数集set(n)中的数如下。
(1) n∈set(n);
(2) 在n的左边加上一个自然数,但该自然数不能超过最近添加的数的一半;
(3) 按此规则进行处理,直到不能再添加自然数为止。
例如,set(6)={6,16,26,126,36,136}。半数集set(6)中有6 个元素。
注意半数集是多重集。
对于给定的自然数n,编程计算半数集set(n)中的元素个数。
个人分析:这是一个递推问题,因为存在关系
因此很轻松地可以编写出代码。这里有一个技巧,就是如果是要求取大量的(比如从1到1000)的半数集的元素个数,用上述递推公式则会重复求取前面求过的半数集的元素个数,因此有个小诀窍就是把已经求好的值放进一个很大的数组比如s[1000],该数组初始化全部元素为0,因此只需要判断s数组中如果元素大于零那么直接可以得到set(n) = s[n]。此外,注意边界条件,set(1) = 1。
代码如下:
int HalfSet(int n){
if(n == 1) return 1;
else{
int sum = 1;
for(int i=1;i<=n/2;i++) sum += HalfSet(i);
return sum;
}
}
2.士兵站队问题
问题描述:
在一个划分成网格的操场上,n个士兵散乱地站在网格点上。网格点由整数坐标(x,y)表示。士兵们可以沿网格边上、下、左、右移动一步,但在同一时刻任一网格点上只能有一名士兵。按照军官的命令,士兵们要整齐地列成一个水平队列,即排列成(x,y),(x+1,y),…,(x+n-1,y)。如何选择x 和y的值才能使士兵们以最少的总移动步数排成一列。请编写程序计算使所有士兵排成一行需要的最少移动步数。
个人分析:题目要求排成水平队列,即最终他们的纵坐标相同,横坐标相互间隔为1。因此,确定纵坐标是比较简单的,只需要求min(|y1-y0|+|y2-y0|+....+|yn-y0|),y0很明显是y1到yn间的中位数(数学证明很简单)。而横坐标我们需要确认队列第一个人的横坐标,然后其他人的坐标就能推出。同理,求出min(|x1-x0|+|x2-(x0+1)|+....+|yn-(y0+n-1)|) ,上式也可以转化为min(|x1-x0|+|(x2-1)-x0|+....+|((xn-(n-1))-x0|),可以先对各点的x坐标做相应处理再求中位数,最后再用公式求出移动步数。
代码如下:
#include <iostream>
#include <cmath>
using namespace std;
struct point{
int x;
int y;
};
/*这是插入排序*/
void SortY(point *p,int len){
int i,j;
int key;
for(i = 1;i<len;i++){
key = p[i].y;
/*insert A[i] into the sequence A[1,...i-1]*/
j=i-1;
while(j >= 0 && p[j].y > key){
p[j+1].y = p[j].y;
j = j-1;
}
p[j+1].y = key;
}
}
void SortX(point *p,int len){
int i,j;
int key;
for(i = 1;i<len;i++){
key = p[i].x;
/*insert A[i] into the sequence A[1,...i-1]*/
j=i-1;
while(j >= 0 && p[j].x > key){
p[j+1].x = p[j].x;
j = j-1;
}
p[j+1].x = key;
}
}
int main(){
int n;
cin>>n;
point *p=new point[n];
for(int i=0;i<n;i++) cin>>p[i].x>>p[i].y;
SortY(p,n);
SortX(p,n);
int ym = p[n/2].y;
int movesum = 0;
for(int i=0;i<n;i++){
movesum += int(abs(double(p[i].y)-double(ym)));
}
for(int i=0;i<n;i++){
p[i].x = p[i].x - i;
}
SortX(p,n);
int xk = p[n/2].x;
for(int i=0;i<n;i++){
movesum += int(abs(double(p[i].x)-double(xk)));
}
cout<<movesum;
}
3.双色汉诺塔问题
问题描述:
设A、B、C是3 个塔座。开始时,在塔座A 上有一叠共n 个圆盘,这些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号为1,2,……,n,奇数号圆盘着蓝色,偶数号圆盘着红色,如图所示。现要求将塔座A 上的这一叠圆盘移到塔座B 上,并仍按同样顺序叠置。在移动圆盘时应遵守以下移动规则:
规则(1):每次只能移动1 个圆盘;
规则(2):任何时刻都不允许将较大的圆盘压在较小的圆盘之上;
规则(3):任何时刻都不允许将同色圆盘叠在一起;
规则(4):在满足移动规则(1)-(3)的前提下,可将圆盘移至A,B,C 中任一塔座上。
试设计一个算法,用最少的移动次数将塔座A 上的n个圆盘移到塔座B 上,并仍按同样
对于给定的正整数n,编程计算最优移动方案。
个人分析:看似很复杂,问题实际与汉诺塔没区别。
代码如下:
#include <iostream>
#include <cmath>
using namespace std;
void move(int n,char source,char target){
cout<<n<<" "<<source<<" "<<target<<endl;
}
void hanoi(int disks, char source,char temp, char target){
if (disks == 1){
move(disks,source,target);
}
else{
hanoi(disks-1,source,target,temp);
move(disks,source,target);
hanoi(disks-1,temp, source,target);
}
}
int main(){
int disks;
cin>>disks;
hanoi(disks,'A','C','B');
}