翻硬币
翻硬币
题目描述:
一摞硬币共有m枚,每一枚都是正面朝上。取下最上面的一枚硬币,将它翻面后放回原处。然后取下最上面的2枚硬币,将他们一起翻面后再放回原处。再取3枚,取4枚……直至m枚。然后再从这摞硬币最上面的一枚开始,重复刚才的做法。这样一直做下去,直到这摞硬币中的每一枚又都是正面朝上为止。例如,m为1时,翻两次即可。m为2时,翻3次即可;m为3时,翻9次即可;m为4时,翻11次即可;m为5时,翻24次即可;…;m为30时,翻899次即可;…
输 入:
仅有的一个数字是这摞硬币的枚数m,0<m<1000。
输 出:
为了使这摞硬币中的每一枚又都是正面朝上所必需翻的次数。
输入样例:
30
输出样例:
899
程 序:
program Programl;
var m:integer;
function solve (m:integer):integer;
vat i,t,d:integer;
flag:boolean;
begin
if (m=1)then
so1ve:= ①
else begin
d:=2*m+1;
t:= 2;
i:= 1;
flag:=False;
repeat
if (t=1)then
begin
solve:= ②
flag:=True;
end
else if ( ③ )then
begin
solve:=i*m-1;
flag:=True;
end
else
t:= ④ ;
i:=i+1;
until flag;
end
end;
begin
read (m);
if ((m>0) and (m<1000)) then
writeln ( ⑤ );
end.
ans:
① 2
② i*m
③ t=2*m
④ (t*2)mod d
⑤ solve(m)
solve:
我们把每一次的翻转称为一次“小翻转”,从顶上开始连续翻n次称为
一次“大翻转”。最后翻到全是正面朝上的状态时,如果用了 a 次大翻转和 b 次小翻转,那么总的翻转次数是 a*n + b。研究一次大翻转的置换结构。自底向上(为方便我们实际上用自左向右),用 1, 2, 3, ..., n 标记 n 个硬币,用 a' 表示硬币 a 的反面朝上状态。我们还在这一摞硬币的右端放一面镜子,那么,初始状态是:('|' 表示镜子的位置)
[1 2 3 4 ... n-1 n | n' (n-1)' ... 4' 3' 2' 1']
经过一次大翻转后,成为:
[2 4 6 ... 5' 3' 1' | 1 3 5 ... 6' 4' 2']
这个变换很有规律。只要令 a'=-a,可以看出,一次大翻转就是把编号
为 c 的硬币变换到 c/2 (mod 2n+1) 的位置。看其逆变缓将更明显。
显然,如果 2^m=1 (mod 2n+1),那么经过 m 次大翻转后,所有硬币都
归到原位而且面朝上。此时共用了 m*n 次小翻转。
显然,如果 2^m=-1 (mod 2n+1),那么经过 m 次大翻转后,所有硬币
都将归到原位,并且正面朝下。如果不做最后那个大翻转的最后那个n
个硬币翻过来的小翻转,那么就能得到正面全朝下的状态,此时需要
m*n-1 次小翻转。剩下的问题是,证明只有上面所述两种情况下才会出现正面全朝上的局面。需要证明几个事实:
1) 进行任意次大翻转后,再进行 0 < b < n-1 次小翻转,那么不可
能出现全部硬币正面朝上的局面。
(即要出现正面全朝上的情况,必然有 b=0 或 b=n-1.)
2) 如果经过 m 次大翻转后全部硬币正面朝上,那么确实每个硬币都
回到了原来的位置。
3) 如果经过 m 次大翻转后全部硬币正面朝下,那么确实每个硬币都
回到了原来的位置。
2) 与 3) 比较容易证明,证法也类似。1) 证起来麻烦一些。当然可以用数学归纳法来证明。
这个就是这个程序的思路,有了思路,我想你这个T和D应该就知道是什么意思了吧?
[问题描述]
一摞硬币 共有m枚,每一枚都是正面朝上。取下最上面的一枚硬币,将它翻面后放回原处。然后取下最上面的2枚硬币,将他们一起翻面后再放回原处。再取3枚,取4 枚……直至m枚。然后再从这摞硬币最上面的一枚开始,重复刚才的做法。这样一直做下去,直到这摞硬币中的每一枚又都是正面朝上为止。例如,m为1时,翻两 次即可。m为2时,翻3次即可;m为3时,翻9次即可;m为4时,翻11次即可;m为5时,翻24次即可;…;m为30时,翻899次即可;…
输 入:
仅有的一个数字是这摞硬币的枚数m,0<m<1000。
输 出:
为了使这摞硬币中的每一枚又都是正面朝上所必需翻的次数。
某单元格输入:
30
某单元格等于:
899
int GetTurnAfterPos(int n, int turn, int m)
{
int TurnNum ;//每轮翻转次数
int TurnAfterPos; //每轮循环后的位置
TurnNum = turn - n + 1;
if(TurnNum % 2 == 0) //为偶数次循环
TurnAfterPos = n + TurnNum / 2;
else //为奇数次循环
TurnAfterPos = (TurnNum + 1) / 2;
return TurnAfterPos;
}
int solve(int turn, int * CurrentPos, int m)
{
if(turn == 0)
return 0;
{
int RetNum = GetTurnAfterPos(i+1, turn, m);
CurrentPos[RetNum-1] = CurrentPos[m+i];
}
for(i = 0; i < m; i ++)
{
CurrentPos[i+m] = CurrentPos[i];
}
int main()
{
int m = 30;//硬币总数
int * CurrentPos = new int[m*2];
int n = 0;//翻转次数
bool flag = false;
do
{
n++;
for(int i = 0; i < m*2; i++)
CurrentPos[i] = i % m + 1;
int y = n % m;
solve(m, CurrentPos, m);
solve(y, CurrentPos, m);
{
printf("%d ",CurrentPos[i]);
}
//solve(5, CurrentPos, m);
printf("/n");
{
if(CurrentPos[i] != i+1)
{
break;
}
if(i == m - 1 && n > 1)
flag = true;
}
}
while(!flag);
printf("翻转的总次数是: %d/n", n - 1);
else
printf("翻转的总次数是: %d/n", n);
return 0;
}
{
bool * CurrentSurface = new bool[m];
for(int i = 0; i < m; i++)
CurrentSurface[i] = true;
bool bSuccess = false;
do
{
for(int i = 0; i < m; i++)
{
for(int j = 0; j < (i+1) / 2; j ++)
{
//turnCoin(j, i - j + 1);
bool temp = CurrentSurface[j];
CurrentSurface[j] = !CurrentSurface[i - j];
CurrentSurface[i - j] = !temp;
}
if((i+1) % 2 == 1)
CurrentSurface[i/2] = !CurrentSurface[i/2];
for(int n = 0; n < m; n ++)
{
if (CurrentSurface[n] == false)
{
bSuccess = false;
break;
}
}
break;
}
printf("翻转的总次数是: %d/n", turnTimes);
delete [] CurrentSurface;
}
{
int CoinCount = 1;
while(CoinCount > 0)
{
printf("请输入硬币的总数:");
scanf("%d",&CoinCount);
solve(CoinCount);
}
}
{
int m;
do {
scanf("%d", &m);
if(m > 0 && m < 1000)
printf("%d/n/n", solve(m));
} while(m > 0 && m < 1000);
}
int solve(int m)
{
int I,
t,
d,
s = - 1; //翻转的次数
int flag;
//如果只有一枚硬币,翻两次就能达到目标
if(m == 1)
s = 2;
else
{
d = 2 * m + 1; //确定硬币是经过偶数次翻转还是奇数次翻转
t = 2; //表示一个COIN必须翻转偶数次,才能从正面继续翻回到正面。
I = 1; //翻转的轮数,每轮为从1翻转到m
flag = 0; //退出循环标志,翻转完成标志
printf("d = %d:/n", d);
do {
printf("/tt(%d) = %d, /t s: %3d, %3d/n", I, t, I * m, I * m - 1);
if(t == 1) //
{
s = I * m;
flag = 1;
}
else if(t == 2 * m) {
s = I * m - 1;
flag = 1;
}
else
t = (t * 2) % d;
}
} while(!flag);
}
printf("s = %d, I = %d, t = %d, d = %d/n", s, I, t, d);
return s;
}
由 以上分析得到了,任意位置pos,经过一轮番转后的位置变为了new_pos = num/2 + 1 + turnNum/2 - turnNum%2*turnNum 所以,可以由上式计算出new_pos。那么,如果new_pos也为1的话,则所有的硬币都回到了原来的位 置,且都朝正面。
#include "stdio.h"
int solve(int m)
{
int I = 0;
int pos,turnNum,temp,s;
turnNum = 0;
if(m == 1) s = 2;
else
{
pos = 1;
I = 0;
do
{
I++;
temp = m+1-pos;
turnNum += temp;
pos = m/2 + 1 + pos/2 - temp%2*(pos/2)*2;
}while(pos!=1);
s = I*m - turnNum%2; //the sum of times
}
return s;
}
main()
{
int m;
do
{ scanf("%d",&m);
printf("the sum is : %d/n",solve(m));
}while(m != -1);
}
#include<stdlib.h>
#define swap(a,b){int t=!a; a=!b; b = t;}
int flag[1000];
bool isright()
{
for(int i=0; i<n; i++)
if(!flag[i])
return false;
return true;
}
int turncoin()
{
int total = 1;
while(1)
{
int k = total%n;
int i,j;
for( i=0,j=k; i<=j; i++, j--)
swap(flag[i],flag[j]);
total++;
if(isright())
return total;
}
return total;
}
void init()
{
for(int i=0; i<n; i++)
flag[i] = 1;
flag[0] = 0;
}
int main()
{
while(std::cin >> n && n>0)
{
init();
std::cout << turncoin() << std::endl;
}
return 0;
}
设硬币从最上一枚到最底下一枚编号为1到n,且从1到n的m次翻转为一轮,则经过一轮翻转后原来在i位置的硬
币Ci翻转到新位置为f(i), 在这一轮翻转中Ci换面次数为g(i),容易得到:
f(i) = (m-i)/2+1, 当m-i为偶数; (m+i+1)/2, 当m-i为奇数;
g(i) = m+1-i
考虑一轮翻转对应的置换H(m)(循环形式):
例如H(4) = (1,3,4)(2)
H(5) = (1,3,2,4,5)
H(10) = (1,6,3,7,9,10)(2,5,8)(4)
置换的阶等于其各个循环的阶的最小公倍数。对任意置换,若阶为k,则它的k次幂为单位置换。
有可以证明的几个重要结论:
<1>:设置换H(m)的阶为k,则经过k轮翻转后,所有硬币重新回到起始位置,且正反面一致。若全部为反面,则
由翻转的可逆性知前一次翻转的状态恰好为整摞硬币的逆序且全为正面。
<2>:置换H(m)的阶为1所在的循环的阶。
/*
* poj1662 flip Coins 翻硬币问题
* happynp 12.21.2008
*/
#include<iostream>
using namespace std;
__int64 flip(int m)
{
int p = 1; // 跟踪最上面的硬币设其位置是1
int k=0; // 翻转轮数
int c=0; // 累计换面次数
do {
k++; // 求1所在的循环的阶
c += (m+1-p)&0x1;
// 翻转一轮后硬币的位置
if(((m-p)&0x1)==0) // m-p为偶数
p = ((m-p)>>1)+1;
else
p = (m+p+1)>>1;
}while(p!=1);
__int64 ret = k;
ret = ret*m-(c&0x1);
return ret;
}
int main()
{
int t, m;
scanf("%d", &t);
while(t-->0) {
scanf("%d", &m);
if(m==1) printf("%d\n", 2);
else printf("%I64d\n", flip(m));
}
return 0;
}