/**/
/*
洗牌问题
设2n张牌分别标记为1, 2, ..., n, n+1, ..., 2n,初始时这2n张牌按其标号从小到大排列。经一次洗牌后,原来的排列顺序变成n+1, 1, n+2, 2, ..., 2n, n。即前n张牌被放到偶数位置2, 4, ..., 2n,而后n张牌被放到奇数位置1, 3, ..., 2n-1。可以证明对于任何一个自然数n,经过若干次洗牌后可恢复初始状态。现在你的的任务是计算对于给定的n的值(n≤10^5),最少需要经过多少次洗牌可恢复到初始状态。
输入输出格式
输入数据由多组数据组成。每组数据仅有一个整数,表示n的值。
对于每组数据,输出仅一行包含一个整数,即最少洗牌次数。
样例输入
10
样例输出
6
*/
NO. 1 直接用一个一维数组模拟,速度超慢,代码如下:
#include < stdio.h >
#define MAX 20002
int main( void )
... {
int a[MAX]=...{0},n2;
int i,j,k,n,t,total=0;
scanf("%d",&n) ;
n2 =n*2 ;
for(i=1 ; i<= n2 ; i++)
a[i] = i ;
while (1)
...{
for(i=n+1,k=1; i<= n2 ; i++,k+=2)
...{
t = a[i] ;
for(j=i-1 ; j>= k ; j--)
a[j+1] = a[j] ;
a[k] = t ;
/**//*for(j=1 ; j<= n2 ; j++)
printf("%d ",a[j]);
printf(" ");*/
}
total ++ ;
if ( a[1] == 1)
break ;
}
printf(" total = %d ",total);
system("pause");
return 0 ;
}
NO. 2 用两个一维数组模拟,速度大大提高:代码如下:
#include < stdio.h >
#include < time.h >
#define MAX 20002
int main( void )
... {
clock_t end,begin ;
int a[MAX]=...{0},b[MAX],n2;
int i,j,k,n,t,total=0;
scanf("%d",&n) ;
begin = clock();
n2 =n*2 ;
for(i=1 ; i<= n ; i++)
a[i] = i ;
for(j=n+1,i=1; j<= n2 ; j++,i++)
b[i] = j ;
while (1)
...{
for(i=n,j=n; i>= 1; i--,j--)
...{
t= i+i ;
a[t] = a[i] ;
a[t-1]=b[j] ;
}
for(i=n+1,j=1; i<= n2 ; j++,i++)
b[j] = a[i] ;
total ++ ;
if ( a[1] == 1)
break ;
}
printf("total = %d ",total);
end = clock();
printf("%d has cost %lf seconds!!! ",n,(double)(end-begin)/CLOCKS_PER_SEC);
system("pause");
return 0 ;
}
NO. 3 找规律,然后简化过程,你会发现:其它只要1还原了,那么其它的也会还原,因此只要找到1还原时停止就OK了
/**/ /*
这题目,比较经典,找规律的,只要找到a[1] = 1 时停止就OK了。。。。
简写程序如下:
*/
#include < stdio.h >
int main( void )
... {
int total=1,a1=2; /**//*找到了第一个位置第一次出现1时就停止,此时即洗牌完成*/
int n,k;
scanf("%d",&n) ;
k = n<<1 ;
while ( a1 != 1)
...{
if(a1 <= n)
a1 <<= 1 ;
else
a1 = (a1<<1)-k-1 ;
total ++;
}
printf("%d ",total) ;
return 0;
}
洗牌问题
设2n张牌分别标记为1, 2, ..., n, n+1, ..., 2n,初始时这2n张牌按其标号从小到大排列。经一次洗牌后,原来的排列顺序变成n+1, 1, n+2, 2, ..., 2n, n。即前n张牌被放到偶数位置2, 4, ..., 2n,而后n张牌被放到奇数位置1, 3, ..., 2n-1。可以证明对于任何一个自然数n,经过若干次洗牌后可恢复初始状态。现在你的的任务是计算对于给定的n的值(n≤10^5),最少需要经过多少次洗牌可恢复到初始状态。
输入输出格式
输入数据由多组数据组成。每组数据仅有一个整数,表示n的值。
对于每组数据,输出仅一行包含一个整数,即最少洗牌次数。
样例输入
10
样例输出
6
*/
NO. 1 直接用一个一维数组模拟,速度超慢,代码如下:
#include < stdio.h >
#define MAX 20002
int main( void )
... {
int a[MAX]=...{0},n2;
int i,j,k,n,t,total=0;
scanf("%d",&n) ;
n2 =n*2 ;
for(i=1 ; i<= n2 ; i++)
a[i] = i ;
while (1)
...{
for(i=n+1,k=1; i<= n2 ; i++,k+=2)
...{
t = a[i] ;
for(j=i-1 ; j>= k ; j--)
a[j+1] = a[j] ;
a[k] = t ;
/**//*for(j=1 ; j<= n2 ; j++)
printf("%d ",a[j]);
printf(" ");*/
}
total ++ ;
if ( a[1] == 1)
break ;
}
printf(" total = %d ",total);
system("pause");
return 0 ;
}
NO. 2 用两个一维数组模拟,速度大大提高:代码如下:
#include < stdio.h >
#include < time.h >
#define MAX 20002
int main( void )
... {
clock_t end,begin ;
int a[MAX]=...{0},b[MAX],n2;
int i,j,k,n,t,total=0;
scanf("%d",&n) ;
begin = clock();
n2 =n*2 ;
for(i=1 ; i<= n ; i++)
a[i] = i ;
for(j=n+1,i=1; j<= n2 ; j++,i++)
b[i] = j ;
while (1)
...{
for(i=n,j=n; i>= 1; i--,j--)
...{
t= i+i ;
a[t] = a[i] ;
a[t-1]=b[j] ;
}
for(i=n+1,j=1; i<= n2 ; j++,i++)
b[j] = a[i] ;
total ++ ;
if ( a[1] == 1)
break ;
}
printf("total = %d ",total);
end = clock();
printf("%d has cost %lf seconds!!! ",n,(double)(end-begin)/CLOCKS_PER_SEC);
system("pause");
return 0 ;
}
NO. 3 找规律,然后简化过程,你会发现:其它只要1还原了,那么其它的也会还原,因此只要找到1还原时停止就OK了
/**/ /*
这题目,比较经典,找规律的,只要找到a[1] = 1 时停止就OK了。。。。
简写程序如下:
*/
#include < stdio.h >
int main( void )
... {
int total=1,a1=2; /**//*找到了第一个位置第一次出现1时就停止,此时即洗牌完成*/
int n,k;
scanf("%d",&n) ;
k = n<<1 ;
while ( a1 != 1)
...{
if(a1 <= n)
a1 <<= 1 ;
else
a1 = (a1<<1)-k-1 ;
total ++;
}
printf("%d ",total) ;
return 0;
}