回 帖 发 新 帖 刷新版面

主题:如何实现这个问题?

一个很有意思的问题,貌似很简单,但是我自己确实没想出来,看来也不是太简单啊。希望高手给出个算法,


有2N个不同的数,如k1,k2,k3.....k2N, 将其分为N对,(k1',k2')....(k2N-1',k2N'),如何实现呢? 
如果N=2  
1 2 3 4--》(1,2),(3,4)|(1,3),(2,4)|(1,4),(2,3);
如果N=3
(1,2)(34)(56)
(1,2)(35)(46)
(1,2)(36)(45)
(1,3)(24)(56)
(1,3)(25)(46)
(1,3)(26)(45)
(1,4)(23)(56)
(1,4)(25)(36)
(1,4)(26)(35)
(1,5)(23)(46)
(1,5)(24)(36)
(1,5)(26)(34)
(1,6)(23)(45)
(1,6)(24)(35)
(1,6)(25)(34)

如果N取任意的数呢?
谢谢啦。

回复列表 (共11个回复)

沙发

说下我自己的思路吧,没时间去写代码了,这样的分组含着明显的遍历,要得到所有解的问题。  对于遍历的问题,有如下算法,递归,记忆递归,回溯,等、、
这里从1开始,依次往后试解。一般可考虑回溯的算法,如:

先找到1,然后可以是2(这样以递增的次序保证全部遍历到),往后的数不能与已出现的数相同(用一个标志量记录是否出现重复就可以了,如果重复,往后试一个新数,否则就用当前数)。知道分完一组,然后同样的方法分第二组,直到解完所有分组,然后输出一组解,重复解完所有解、

有空贴上代码。。

板凳


谢谢啊,不好意思还是没太理解你的意思,我也没接触过算法的相关知识,恳请你贴一下代码或者具体说一下算法吧?

3 楼

貌似N不能任意吧,过大会溢出。

4 楼

当然不能无限大了,但是程序应该有通用性,小的话很容易列举,大了就不好办了。

5 楼

好像不是分成N对吧

6 楼

理解错了,不好意思

7 楼

#include <stdio.h>
#include <conio.h>

void Copy(int a[100],int b[100]);
void DisPlay(int a[100],int m,int n);
void PrintfData(int a[255][100], int n,int num);

void main()
{
    int a[255][100];
    int i,j,temp,n,num;  //num为得到的组数,i,j为循环变量,temp是用到的一个临时变量
    a[0][0] = 1;
    a[0][1] = 2;
    num = 1;
    printf("你说N是多少吧:");
    scanf("%d",&n);
    for (i = 2; i <= n; i++) //从n为1算起,由于为1进行了初始化,这里从2开始
    {
        for(j = 0; j < num; j++)
        {
            a[j][2*i - 2] = 2*i - 1;
            a[j][2*i - 1] = 2*i;
        }
        num *= (2*i - 1);
        for(temp = j; temp < num; temp++)
        {
            Copy(a[temp],a[temp % j]);
            DisPlay(a[temp],2*i - 1,temp / j);
        }
    }
    PrintfData(a, n, num);
    printf("共有%d组数",num);
    getch();
}
void Copy(int a[100],int b[100]) //将b数组的数据复制到a
{
    int i;
    for (i = 0; i < 100; i++)
    {
        a[i] = b[i];
    }
}

void DisPlay(int a[100],int m,int n) //将a数组的倒数第m个数据与第n个调换位置
{
    int i;
    i = a[m - 1];
    a[m - 1] = a[n - 1];
    a[n - 1] = i;
}

void PrintfData(int a[255][100],int n,int num) //打印函数
{
    int i,j;
    for (i = 0; i <num; i++)
    {
        for (j = 0; j < n; j++)
        {
            printf("(%d %d)",a[i][2*j],a[i][2*j + 1]);
        }
        printf("\n");
    }
}

8 楼

只能支持n最大为4

9 楼

程序可能有点难看,说下思路吧.大体是先得到n=1的数据,然后通过n=1算n=2时的数据.n每加1,得到的分组数自成2n-1,由于一维数组最多能存255组数据,由此n最大只能算4,如果能提升到3维数组的话,应该能算到更大的n.这里的二维数组一维存数字的具体排列.
  最难理解的还是循环内部原理了,例如如何通过n=2算n=3呢?先得到n=2的3组数据,然后n=3的数据组数是由n=2的3组数据乘以5得到的,把这15组数据分成1+4 = 5个部分,第1部分直接在原来3组数据末两位加上4和5,然后后面4个部分是由4分别替换前面的数字得到,我们把n=2得到的3组数据命名为数据1,2,3,则第二部分的数据是4分别与数据1,2,3中的第一个数对调得到的,第三部分是分别与三个数据中的第二个数对调,...以此类推.最终得到n=3的完整数据.

10 楼

其实我觉得这个题目和 全排列的生成算法或八皇后问题 是差不多的。
不知道下面的代码是不是正确:
[code=c]#include <stdio.h>
#include <assert.h>

#define NL ((unsigned char)'\n')

#ifndef  Bool
# define Bool  unsigned int
# define TRUE  (1U)
# define FALSE (0U)
# define YES   TRUE
# define NO    FALSE
#endif

/* 检查在numbers的第pos个位置是否符合要求
 * 
 *  isused: 记录了哪些数已经被使用了
 *  为了保证输出的数据不重复,必须保证:
 *    如果 pos 为偶数,那么必须大于它前面那个数
 *    如果 pos 为基数,那么必须大于它前面第2个数
 ***/
static inline Bool
check( int numbers[ ], int isused[ ], int pos, int n )
{
  int _tmp = pos % 2;
  return  isused[ numbers[pos] ] == YES       ?   FALSE
      :   numbers[pos] > numbers[pos-1-_tmp]  ?   TRUE   
      :   FALSE;
}


/* 输出numbers数组中的数据,每2个一组,各组间用括号分隔
***/
static inline void
show( int numbers[ ], int n )
{
  int n_ = 0;
  putchar( NL );
  for ( n_ = 1; n_ < 2 * n; n_ += 2 )
    printf( "(%d,%d) ", numbers[n_], numbers[n_+1] );
}

/* 初始数据,把所有的位置都标记为已经使用
 * 把numbers初始为1,2,3,4 ... 2n
 * 这样即得到第一组符合要求的数
 ***/
static inline void
inital( int numbers[ ], int isused[ ], int n )
{
  int n_ = 0;
  while( ++n_ <= 2*n )
    numbers[ n_ ] = n_,
    isused [ n_ ] = YES;
}


#define CHECK(pos)   check( numbers, isused, pos, n )

/*    foo: 这个函数的重点在for循环里面的if-else-if等语句 
 ***/
int
foo( const int n )
{
  assert( n > 0 );
  int cur, count;
  int numbers[ 2*n + 2 ];
  int isused [ 2*n + 2 ];
 
  if ( n > 8 ) /* 如果n太大,速度将很慢  */
    {
      printf( "foo: error:  n is too large. " );
      return 0;
    }
  
  cur   = 2*n+1;
  count = 0;
  inital( numbers, isused, n );

  for ( ; cur > 1 ; )
  {
      if ( cur > 2*n )
        {
          count ++;
          show( numbers, n );
          isused[ numbers[ --cur ] ] = NO;
          numbers[ cur ] ++;
        }
      else
        {
          if ( numbers[ cur ]  >  2*n )
             /* 如果这个数大于最大的数2*n,那么要回溯到cur-1个位置继续寻找 */
             /* 其实numbers[cur]也是可以从2开始找的,不过找到的数据会重复  */
             /* 把第cur-1个数解除标记,即isused[ numbers[ --cur ] ] = NO   */
             /* 从cur-1这个位置继续寻找, 即numbers[ cur ] ++;             */
             numbers[ cur ] = ( cur + 1 ) / 2,
             isused[ numbers[ --cur ] ] = NO,
             numbers[ cur ] ++;
          else if ( CHECK(cur) == TRUE ) 
             /* 如果这个数满足要求,寻找下一个位置上数并标记这个数已被使用 */
             isused[ numbers[ cur++ ] ] = YES;
          else   
             /* 不满足,numbers的第cur个数 加1,直到满足为止 */
             numbers[ cur ] ++;
        }
  }

  return count;
}

int main( void )
{
  int n, count;
  puts( "input the valu of n: " );
  scanf( "%d", &n );
  count = foo( n );
  printf( "\n\ncount: %d ", count );
  return( 0 );
}
 
[/code]

我来回复

您尚未登录,请登录后再回复。点此登录或注册