算法设计与分析习题2的部分解答

2-2:第5个应该是对的,其余的不是死循环就是搜索结果不正确。

2-3:每次记录middle的位置,再注意(x < a[0])和(x > a[n-1])的情况即可。

2-8:每次比较a[middle]与middle的值即可:

 while( left <= right )
 {
    middle = (left+right) / 2;
    if( middle == a[middle] ) return middle;
    else if( middle > a[middle] ) left = middle + 1;
    else right = middle - 1;
 }

2-11:每个元素的下一个位置是可以计算出来的,令t = n - 1 - k; 则第i个元素将被移到第(i+t)%n个位置上去,算法开始从第0个位置开始,只要保证移动的位置没有重复并且移动了n次则退出,否则从1个位置开始移动直到移动了n次,如果循环就以此类推。(不必判断每个元素是否移动过,因为在移动n次前不会有重复的元素),因此O(n)的时间,O(1)的空间:

#include <stdio.h>

#define N 100

int main()
{
  int a[N], n, k, i, j, temp, count;
  while( scanf( "%d%d", &n, &k ) != EOF )
  {
    for( i = 0; i < n; i++ )
      scanf( "%d", a + i );
    k = n - 1 - k; //用来计算元素的下一个位置
    for( i = 0, count = 0; i < n && count < n; i++ ) //count用来记录所移动的次数
    {
      temp = a[i], j = (i+k) % n;
      while( j != i ) //判断是否产生了循环
      {
        a[j]^=temp^=a[j]^=temp; //交换两个数,temp记录下一个位置的数。
        j = (j+k) % n;
        count++;
      }
      a[j]^=temp^=a[j]^=temp;
      count++;
    }
    for( i = 0; i < n; i++ )
      printf( "%d ", a[i] );
    printf( "/n" );
  }
  return 0;
}

2-15:分组比较即可。

#include <stdio.h>

void MaxMin( int, int, int&, int& );
int a[100];

int main()
{
 int max, min, n, i;
 while( scanf( "%d", &n ) != EOF )
 {
  for( i = 0; i < n; i++ )
   scanf( "%d", a + i );
  MaxMin( 0, n-1, max, min );
  printf( "%d %d/n", max, min );
 }
 return 0;
}

void MaxMin( int i, int j, int &max, int &min )
{
 int middle;
 int x1, y1, x2, y2;
 if( i == j )
 { max = min = a[i]; return;  }
 if( j - i == 1 )
 { 
  if( a[i] > a[j] ) max = a[i], min = a[j];
  else    max = a[j], min = a[i];
  return;
 }
 middle = (i+j) / 2;
 MaxMin( i, middle, x1, y1 );
 MaxMin( middle+1, j, x2, y2 );
 if( x1 > x2 )
 { max = x1; }
 else
 { max = x2; }
 if( y1 > y2 )
 { min = y2; }
 else
 { min = y1; }
}

2-16:和锦标赛排序类似的思想。第一次找最小值是n-1,然后找次小值根据以前的信息,由完全二叉树的高度为[logn]+1,则可以得出比较次数为[logn]-1,则总比较次数为n + [logn] - 2.  

2-17:对于一个集合,O(n)的扫描即可,每次排好一个位置。数组下标从1开始。

for (i = 1; i <= n; )
 if (a[i] != i)
 {
  temp = a[a[i]];
  a[a[i]] = a[i];
  a[i] = temp;
 }
 else
  i++;

k个集合复杂度为O(kn)(k为常数)。

2-22:经典的方法:

for (i = 0; i < n; i++)
 swap(a[i], a[rand()%n]);

2-30:只要找出y坐标的中位数即可:

找中位数的方法:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N 100
void Rand_select( int*, int, int );
int partition( int*, int, int );
int swap( int&, int& );
int k, ans;

int main()
{
 int n, a[N], i;
 while( scanf( "%d", &n ) != EOF )
 {
  srand(time(NULL));
  k = n/2;  
  for( i = 0; i < n; i++ )
   scanf( "%d", a + i );
  Rand_select( a, 0, n-1 );
  printf( "%d/n", ans );
 }
 return 0;
}

void Rand_select( int a[], int p, int q )
{
 int m;
 if (p <= q)
 {
  m = partition( a, p, q );
  if( k == m )
  { ans = a[m]; return;  }
  else if( k > m )
   Rand_select( a, m+1, q );
  else
   Rand_select( a, p, m-1 );
 }
}
 
int partition( int a[], int p, int q )
{
 int last, i;
 if (p != q)
  swap( a[rand()%(q-p)+p], a[p] );
 for( i = p+1, last = p; i <= q; i++ )
  if( a[i] < a[p] )
   swap( a[i], a[++last] );
 swap( a[last], a[p] );
 return last;
}

int swap( int &p, int &q )
{
 int temp = p;
 p = q;
 q = temp;
 return 0;
}

2-31:Hash或BST都可以,以下给出BST的代码O(nlogn)

#include <stdio.h>
#include <stdlib.h>

#define N 100
typedef struct TYPE
{
 struct TYPE *left, *right;
 int times, no; //times记录每个元素出现的次数,no记录每个无数的值。
}Node;

int a[N], n, max, key;
Node *root;
void add( Node**, int );
void search( Node* );

int main()
{
 int i;
 while( scanf( "%d", &n ) != EOF )
 {
  root = NULL, max = 0;
  for( i = 0; i < n; i++ )
  {
   scanf( "%d", a + i );
   add( &root, a[i] ); //将每个结点加入到二叉树中(O(nlogn))
  }
  search( root ); //中序遍历找出出现次数最多的元素。(O(n))
  printf( "%d/n", key );
 }
 return 0;
}

void add( Node **r, int m )
{
 if( *r == NULL )
 {
  (*r) = (Node*)malloc(sizeof(Node));
  (*r)->times = 1;
  (*r)->no = m;
  (*r)->right = (*r)->left = NULL;
  return;
 }
 else if( m == (*r)->no )
 {
  (*r)->times++;
  return;
 }
 else if( m > (*r)->no )
  add( &((*r)->right), m );
 else
  add( &((*r)->left), m );
}
 
void search( Node *r )
{
 if( r != NULL )
 {
  search( r->left );
  if( r->times > max ) max = r->times, key = r->no;  
  search( r->right );
 }
}

2-33:这题用分治法感觉有点勉强,回溯+贪心已经非常快了。下面是一种实现,方法不是很好。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define N 20

typedef struct TYPE
{
 int x, y, w;
}PRI; 
int di[8][2] = {{-2,1}, {-1,2}, {1,2}, {2,1}, {2,-1}, {1,-2}, {-1,-2}, {-2,-1}};
int n, flag, used[N][N], ans[N][N]; //used记录某个点是否已经走过, ans记录第i步的坐标。
PRI Queue[N*N][8]; //优先队列的一种,8个方向的位置按出口度排序。

void search( int, int, int );
int swap( int*, int* );
int Weight( int, int );

int main()
{
 int i, j;
 while( scanf( "%d", &n ) != EOF )
 {
  memset( used, 0, n*n*sizeof(used[0][0]) );
  used[0][0] = ans[0][0] = 1, flag = 0;
  search( 1, 0, 0 );
  if( !flag )
   printf( "IMPOSSIBLE/n" );
  else
  {
  for( i = 0; i < n; i++ )
   {
   for( j = 0; j < n; j++ )
    printf( "%4d", ans[i][j] );
    printf( "/n" );
   }      
  }   
 }
 return 0;
}

void search( int depth, int x, int y )
{
 int a, b, i, j;
 if( depth == n*n )
 {
  flag = 1;
  return;
 }
 for( i = 0; i < 8; i++ )
 {
  Queue[depth][i].x = x+di[i][0];
  Queue[depth][i].y = y+di[i][1];
  Queue[depth][i].w = Weight( x+di[i][0], y+di[i][1] );
 }
 for( i = 0; i < 8; i++ )
  for( j = i+1; j < 8; j++ )
   if( Queue[depth][i].w < Queue[depth][j].w )
   {
    swap( &(Queue[depth][i].x), &(Queue[depth][j].x) );
    swap( &(Queue[depth][i].y), &(Queue[depth][j].y) );
    swap( &(Queue[depth][i].w), &(Queue[depth][j].w) );
   }
 for( i = 0; i < 8; i++ )
 {
  a = Queue[depth][i].x, b = Queue[depth][i].y;
  if(flag) return;
  if( a >= 0 && a < n && b >= 0 && b < n && !used[a][b] )
  {
   used[a][b] = 1;
   ans[a][b] = depth + 1;
   search( depth + 1, a, b );
   used[a][b] = 0;
  }
 }
}

int Weight( int p, int q )
{
 int i, a, b, count;
 for( i = 0, count = 0; i < 8; i++ )
 {
  a = p+di[i][0], b = q+di[i][1];
  if( a < 0 || a >= n || b < 0 || b >= n || used[a][b] )
   count++;
 }
 return count;
}

int swap( int *p, int *q )
{
 int temp = *p;
 *p = *q;
 *q = temp;
 return 0;
} 

这题下面的这个链接找到一篇论文(求马步图Hamilton圈的最优算法),用的所谓的分治-回溯-合并的方法,可以做到O(n^2)

http://202.109.195.141/chenyan/noi/noi.htm

2-34:这题明显也是可以构造的,就没再去想分治法了。

设n个二进位分别为:

 a[0] a[1] a[2] a[3] ... a[n-1] 则相应的格雷码为:

a[0] a[0]^a[1] a[1]^a[2] ... 第0位直接写下来,然后两两异或即可。

2-35:直接构造就可以了:

设n=6, 则比赛的序列为:

1 2 3 4 5 6: 表示 1-6 2-5 3-4
1 6 2 3 4 5
1 5 6 2 3 4
1 4 5 6 2 3
1 3 4 5 6 2

当n为奇数时,比如n = 5时也是上面的序列,只要将和6的比赛轮空就可以了。

就先写这么多了,部分题就不写了,还有部分题有待思考。


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值