翻硬币问题的多种求解
来源:互联网 发布:post js加密 编辑:程序博客网 时间:2024/06/03 11:00
[问题描述]
一个关于翻硬币的问题,
一摞硬币共有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
一摞硬币共有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 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 solve(int turn, int * CurrentPos, int m)
{
if(turn == 0)
return 0;
for(int i = 0; i < turn; i ++)
{
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 RetNum = GetTurnAfterPos(i+1, turn, m);
CurrentPos[RetNum-1] = CurrentPos[m+i];
}
for(i = 0; i < m; i ++)
{
CurrentPos[i+m] = CurrentPos[i];
}
return 0;
}
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 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 x = n / m;
int y = n % m;
int y = n % m;
for(i = 0; i < x; i++)
solve(m, CurrentPos, m);
solve(y, CurrentPos, m);
solve(m, CurrentPos, m);
solve(y, CurrentPos, m);
for(i = 0; i < m; i ++)
{
printf("%d ",CurrentPos[i]);
}
//solve(5, CurrentPos, m);
printf("/n");
{
printf("%d ",CurrentPos[i]);
}
//solve(5, CurrentPos, m);
printf("/n");
for(i = 0; i < m; i++)
{
if(CurrentPos[i] != i+1)
{
break;
}
if(i == m - 1 && n > 1)
flag = true;
}
}
while(!flag);
{
if(CurrentPos[i] != i+1)
{
break;
}
if(i == m - 1 && n > 1)
flag = true;
}
}
while(!flag);
if(m % 2 == 0)
printf("翻转的总次数是: %d/n", n - 1);
else
printf("翻转的总次数是: %d/n", n);
printf("翻转的总次数是: %d/n", n - 1);
else
printf("翻转的总次数是: %d/n", n);
delete []CurrentPos;
return 0;
}
return 0;
}
[解法二]
思路:不管硬币的次序,只是记住每次的翻转和交换,硬币的初始状态都是TRUE,每个COIN经过多伦的翻转后都变回TRUE,那么就求出了解,这是最简单的思路。
void solve(int m)
{
bool * CurrentSurface = new bool[m];
for(int i = 0; i < m; i++)
CurrentSurface[i] = true;
{
bool * CurrentSurface = new bool[m];
for(int i = 0; i < m; i++)
CurrentSurface[i] = true;
int turnTimes = 0;//翻转次数
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];
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];
turnTimes=turnTimes+1;
bSuccess = true;
for(int n = 0; n < m; n ++)
{
if (CurrentSurface[n] == false)
{
bSuccess = false;
break;
}
}
for(int n = 0; n < m; n ++)
{
if (CurrentSurface[n] == false)
{
bSuccess = false;
break;
}
}
if(bSuccess)
break;
}
break;
}
}while(!bSuccess);
printf("翻转的总次数是: %d/n", turnTimes);
delete [] CurrentSurface;
}
printf("翻转的总次数是: %d/n", turnTimes);
delete [] CurrentSurface;
}
int main()
{
int CoinCount = 1;
while(CoinCount > 0)
{
printf("请输入硬币的总数:");
scanf("%d",&CoinCount);
solve(CoinCount);
}
{
int CoinCount = 1;
while(CoinCount > 0)
{
printf("请输入硬币的总数:");
scanf("%d",&CoinCount);
solve(CoinCount);
}
return 0;
}
}
网上还有几种解法,也同时列了出来:
[解法三]
这个是最难理解的一个算法,因为没有注释,呵呵。
int solve(int m);
int main()
{
int m;
do {
scanf("%d", &m);
if(m > 0 && m < 1000)
printf("%d/n/n", solve(m));
} while(m > 0 && m < 1000);
}
{
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;
}
t = (t * 2) % d;
}
I = I + 1;
} while(!flag);
}
printf("s = %d, I = %d, t = %d, d = %d/n", s, I, t, d);
return s;
}
} while(!flag);
}
printf("s = %d, I = %d, t = %d, d = %d/n", s, I, t, d);
return s;
}
[解法四]
思路: 设 题设的从1到n的翻转为一轮番转,位置 pos, turnNum 为位置pos所需要的翻转次数 ,假设有5个硬币,我们按其位置分别编号 1,2,3,4,5 经过一轮翻转后,变成了5,3,1,2,4;可以看到原来1位置的硬币现在到了3。这一点说明一轮翻转过后,pos的排列是有规律的,另外,任意位置上的元素的翻转次数为turnNum = num+1-pos;
由以上分析得到了,任意位置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);
}
由以上分析得到了,任意位置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<iostream>
#include<stdlib.h>
#define swap(a,b){int t=!a; a=!b; b = t;}
#include<stdlib.h>
#define swap(a,b){int t=!a; a=!b; b = t;}
int n;
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;
}
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;
}
- 翻硬币问题的多种求解
- 翻硬币问题
- 翻硬币问题
- 翻硬币问题
- 翻硬币问题
- 练习使用Java的BigInteger解决矩阵翻硬币问题
- 翻硬币
- 翻硬币
- 翻硬币
- 翻硬币
- 翻硬币
- 翻硬币
- 翻硬币
- 翻硬币
- 翻硬币
- 翻硬币
- 翻硬币
- 翻硬币
- base64命令的用法
- redflag服务器cvs服务的构建
- Web站点风格切换的实现
- ubuntu 3D桌面 设置
- 八大金牌-从菲尔普斯到CMS系统开发、再到卓越网站的建设
- 翻硬币问题的多种求解
- JS日期控件点击调不出来,调出来也隐藏了其它控件
- 20件Win平台不行!!!而Linux能搞定的事情!!!
- 递归打开tree的第一个结点
- 一个上班的周六 一个整理博客的人
- 我对SOA的技术层面理解
- 口琴简介
- 新开BOKE,请大家多多关照.认识天下好友
- FourCC