主题:如何实现这个问题?
freqent
[专家分:0] 发布于 2011-05-03 22:52:00
一个很有意思的问题,貌似很简单,但是我自己确实没想出来,看来也不是太简单啊。希望高手给出个算法,
有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取任意的数呢?
谢谢啦。
最后更新于:2011-05-04 08:54:00
回复列表 (共11个回复)
沙发
fragileeye [专家分:1990] 发布于 2011-05-04 11:11:00
说下我自己的思路吧,没时间去写代码了,这样的分组含着明显的遍历,要得到所有解的问题。 对于遍历的问题,有如下算法,递归,记忆递归,回溯,等、、
这里从1开始,依次往后试解。一般可考虑回溯的算法,如:
先找到1,然后可以是2(这样以递增的次序保证全部遍历到),往后的数不能与已出现的数相同(用一个标志量记录是否出现重复就可以了,如果重复,往后试一个新数,否则就用当前数)。知道分完一组,然后同样的方法分第二组,直到解完所有分组,然后输出一组解,重复解完所有解、
有空贴上代码。。
板凳
freqent [专家分:0] 发布于 2011-05-04 13:18:00
谢谢啊,不好意思还是没太理解你的意思,我也没接触过算法的相关知识,恳请你贴一下代码或者具体说一下算法吧?
3 楼
cxxcomp [专家分:2370] 发布于 2011-05-04 16:54:00
貌似N不能任意吧,过大会溢出。
4 楼
freqent [专家分:0] 发布于 2011-05-04 17:03:00
当然不能无限大了,但是程序应该有通用性,小的话很容易列举,大了就不好办了。
5 楼
killergsm [专家分:90] 发布于 2011-05-06 21:37:00
好像不是分成N对吧
6 楼
killergsm [专家分:90] 发布于 2011-05-06 21:38:00
理解错了,不好意思
7 楼
killergsm [专家分:90] 发布于 2011-05-06 23:49:00
#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 楼
killergsm [专家分:90] 发布于 2011-05-06 23:49:00
只能支持n最大为4
9 楼
killergsm [专家分:90] 发布于 2011-05-07 00:22:00
程序可能有点难看,说下思路吧.大体是先得到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 楼
windy0will [专家分:2300] 发布于 2011-05-07 17:34:00
其实我觉得这个题目和 全排列的生成算法或八皇后问题 是差不多的。
不知道下面的代码是不是正确:
[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]
我来回复