一个算法题,数学大牛们快过来

lucky-lucky 2014-08-07 08:32:08
题目:
有m个人面向南方站成一排(m ≥1),每喊一次口号可以有n个人同时转身一次(1≤n≤m),问共需喊多少次口号所有人最终全部面向北方?
请编写一个函数,函数有两个参数,分别为m和n,函数返回值为最终需要的次数,若经过无穷大次仍然无法全部转向北方,则输出-1.
例1:
m = 6,n =5:
用0表示面向南方,1表示面向北方,如下:

0 0 0 0 0 0
0 1 1 1 1 1
1 1 0 0 0 0
0 0 0 1 1 1
1 1 1 1 0 0
0 0 0 0 0 1
1 1 1 1 1 1

返回6
例2:
m = 3,n = 2:
返回-1
...全文
2371 64 打赏 收藏 转发到动态 举报
写回复
用AI写文章
64 条回复
切换为时间正序
请发表友善的回复…
发表回复
ck2333 2017-03-25
  • 打赏
  • 举报
回复
当m为奇数,n为偶数时:无解; 当m为奇数,n为奇数时:解为m-n+1; 当m为偶数时:如果n<=m/2时,解为m/n; 如果n>m/2时,解为m/(m-n);
sunsmile1225 2017-03-25
  • 打赏
  • 举报
回复
1、如果m%n==0; 结果是 m/n 2、m和n都是偶数 或者都是奇数时 结果: a, if m/n<2 result=3 b, if m/n>2 result=m/n+1; 3、m偶数,n奇数 无解; 4、m奇数,n偶数 结果: a, if m/n<2 result=f( a , b )+2 b, if m/n>2 result=m/n+1+f( a , b ); 备注:f ( a, b) // 就是 在小于等于b的所有奇数里面 ,找到最少的和为 a的奇数个数。eg: a=7,b=3 7=3+3+1 count=3; a=9, b=5 9=5+3+1 count=3; a=2*n-m 这里a 肯定是奇数; b=m%n 这里b肯定是奇数 int f( a, b){ int count =0; while(m!=0) { count=count+m/n; m=m%n; n=n-2; } }
百分之59 2014-08-15
  • 打赏
  • 举报
回复
引用 61 楼 jinhao82 的回复:
[quote=引用 4 楼 brookmill 的回复:] 如果m是奇数,其中每一位取反都要求是奇数次,结果所有位取反的总次数也必须是奇数才行;而如果n是偶数,每次取反都是偶数位。 所以,在m是奇数而n是偶数的情况下,无解。
m=3 n=2 次数=3[/quote] 别理我,没睡醒
百分之59 2014-08-15
  • 打赏
  • 举报
回复
引用 4 楼 brookmill 的回复:
如果m是奇数,其中每一位取反都要求是奇数次,结果所有位取反的总次数也必须是奇数才行;而如果n是偶数,每次取反都是偶数位。 所以,在m是奇数而n是偶数的情况下,无解。
m=3 n=2 次数=3
lim1234521 2014-08-13
  • 打赏
  • 举报
回复
求出m 和 n 的最小公倍数,用公倍数除以m,结果是偶数就不行,奇数就可以。
难题 2014-08-13
  • 打赏
  • 举报
回复
你的反转N个人没有规律,要自己不断尝试,这应该是要求最小能多少次能反转过来
lim1234521 2014-08-13
  • 打赏
  • 举报
回复
公倍数除以n,就是喊口号的次数
707wk 2014-08-12
  • 打赏
  • 举报
回复
这么久了居然还没解决。。。
  • 打赏
  • 举报
回复
引用 55 楼 lovesmiles 的回复:
[quote=引用 54 楼 zjq9931 的回复:] 我想到了翻转杯子的问题: 这个问题简单些: 有三个杯子,每次翻转两个,请问下是否能够全部翻转过来? 标准答案是不能,但是我似乎做到过啊。 我记得看电视的时候也有人做到过。而且还是10几岁的时候,记忆比较模糊了。
呵呵,在数学上面已经证明了不可能的东西你怎么做得到?只能是各种各样的矛招了,打碎它?只翻九十度?换个空间?[/quote] 具体情节记不太清了。可能是每次翻转两个,但不能是同两个。翻回保持原状。 有电视节目上演过这一个。
勤奋的小游侠 2014-08-12
  • 打赏
  • 举报
回复
引用 54 楼 zjq9931 的回复:
我想到了翻转杯子的问题: 这个问题简单些: 有三个杯子,每次翻转两个,请问下是否能够全部翻转过来? 标准答案是不能,但是我似乎做到过啊。 我记得看电视的时候也有人做到过。而且还是10几岁的时候,记忆比较模糊了。
呵呵,在数学上面已经证明了不可能的东西你怎么做得到?只能是各种各样的矛招了,打碎它?只翻九十度?换个空间?
  • 打赏
  • 举报
回复
我想到了翻转杯子的问题: 这个问题简单些: 有三个杯子,每次翻转两个,请问下是否能够全部翻转过来? 标准答案是不能,但是我似乎做到过啊。 我记得看电视的时候也有人做到过。而且还是10几岁的时候,记忆比较模糊了。
c_str 2014-08-12
  • 打赏
  • 举报
回复
这个问题是纯粹数学问题,在数学上有了解法再写程序吧。
苏客达 2014-08-12
  • 打赏
  • 举报
回复
mark一下,需要考虑
moqiguzhu 2014-08-09
  • 打赏
  • 举报
回复
1.这m个数其实没有位置之分,m的数的状态可以用0和1的个数来标记。 2.一旦指定m,m就不会再变化了,所以甚至可以只用0的个数来标记状态,在这里我用所有m个数的和来标记状态(1的个数)。 3.每一次变化n个数,相当于向前搜索。用x表示0的个数,用y表示1的个数。变化n个数对m个数和的影响就是加上一个数。这个数可能有多种可能,取值的上限和0的个数有关,下限和y的个数有关。用max和min来表示上限和下限,具体来说,当x>=n时,max = n;当x<n时,max = x - (n - x) = 2x - n;当y>=n时,min = -n;当y< n 时,min = n - 2y。 4.人工智能课上介绍的搜索算法有很多,BFS,DFS,A-star之类的。BFS能够得到最优解,但是时间、空间开销可能比较大,DFS可能在短时间内找到解,但不能保证是最优的,A-star有很多优点,解是最优的,速度也快,可是需要启发式函数。这里我采用BFS。 程序也写了,但是有一点BUG,我觉得是我用的ArrayList这个数据结构有问题,再说。 思路应该是没有问题的。
nklofy 2014-08-08
  • 打赏
  • 举报
回复
引用 38 楼 nklofy 的回复:
[quote=引用 37 楼 nklofy 的回复:] 第一步,m/n,剩余余数K=m%n 第二步,K为偶数,需要2次翻转。K/2和已经翻转的(n-K/2)同时翻,(n-K/2)翻两次。 则总步数T=m/n+2 第三步,K为奇数 若n为偶数则无解 若n也为奇数,则回溯一步前面的步数为(m/n-1),未翻转的(n+K)/2和已经翻转的(n-(n+K)/2)同时翻,(n-(n+K)/2)翻两次。 总步数为T=m/n+1 证明: m/n求余数K没有问题 剩下是如何用n来翻转K K每次翻转必须借助K以外的位,那些位必须翻转偶数次,才能保证不变 那么首先假设K为偶数。考虑K/2,他们只需要跟(n-K/2)位一起翻转,剩余的一半K/2,跟相同的(n-K/2)位一起翻转。 完成翻转。 若K为奇数,n为奇数,则无法满足偶数次翻转的可能性。必然有至少1个无法翻转的位。 若K、n同为奇数,前面m/n回溯一次,未翻转的位为(K+n),将其分为两半,可以作为2次翻转完毕。
更正: 第一种情况下,m%n为偶数,则总次数为m/n+2 [/quote] 再更正: 补充几种情况 若能m/n整除,则总次数为T=m/n m/n>1才使用以上公式,否则 若m/n=1,则分m%n奇数,总次数1+3=4,m%n偶数,总次数1+2=3 例如: 7/3:7/3+1=3 8/3:8/3+2=4 9/3:9/3=3 7/5: 1+2=3 8/5: 1+3=4 9/5: 1+2=3 11/3:11/3+2=5 12/3:12/3=4 13/3:13/3+1=5 12/4:12/4=3 13/4:无解 14/4:14/4+2=5 12/5:12/5+2=4 13/5:13/5+1=3 14/5:14/5+2=4
nklofy 2014-08-08
  • 打赏
  • 举报
回复
引用 37 楼 nklofy 的回复:
第一步,m/n,剩余余数K=m%n 第二步,K为偶数,需要2次翻转。K/2和已经翻转的(n-K/2)同时翻,(n-K/2)翻两次。 则总步数T=m/n+2 第三步,K为奇数 若n为偶数则无解 若n也为奇数,则回溯一步前面的步数为(m/n-1),未翻转的(n+K)/2和已经翻转的(n-(n+K)/2)同时翻,(n-(n+K)/2)翻两次。 总步数为T=m/n+1 证明: m/n求余数K没有问题 剩下是如何用n来翻转K K每次翻转必须借助K以外的位,那些位必须翻转偶数次,才能保证不变 那么首先假设K为偶数。考虑K/2,他们只需要跟(n-K/2)位一起翻转,剩余的一半K/2,跟相同的(n-K/2)位一起翻转。 完成翻转。 若K为奇数,n为奇数,则无法满足偶数次翻转的可能性。必然有至少1个无法翻转的位。 若K、n同为奇数,前面m/n回溯一次,未翻转的位为(K+n),将其分为两半,可以作为2次翻转完毕。
更正: 第一种情况下,m%n为偶数,则总次数为m/n+2
nklofy 2014-08-08
  • 打赏
  • 举报
回复
第一步,m/n,剩余余数K=m%n 第二步,K为偶数,需要2次翻转。K/2和已经翻转的(n-K/2)同时翻,(n-K/2)翻两次。 则总步数T=m/n+1 第三步,K为奇数 若n为偶数则无解 若n也为奇数,则回溯一步前面的步数为(m/n-1),未翻转的(n+K)/2和已经翻转的(n-(n+K)/2)同时翻,(n-(n+K)/2)翻两次。 总步数为T=m/n+1 证明: m/n求余数K没有问题 剩下是如何用n来翻转K K每次翻转必须借助K以外的位,那些位必须翻转偶数次,才能保证不变 那么首先假设K为偶数。考虑K/2,他们只需要跟(n-K/2)位一起翻转,剩余的一半K/2,跟相同的(n-K/2)位一起翻转。 完成翻转。 若K为奇数,n为奇数,则无法满足偶数次翻转的可能性。必然有至少1个无法翻转的位。 若K、n同为奇数,前面m/n回溯一次,未翻转的位为(K+n),将其分为两半,可以作为2次翻转完毕。
wangxicoding 2014-08-08
  • 打赏
  • 举报
回复
引用 28 楼 wangxicoding 的回复:
看了前面人的思路受到了启发,说一下我的思路 : 假设定义解题状态开始为(m, 0) ,最终要到达(0, m)状态。 括号里面第一个数代表0的个数,后面代表1的个数。 1. m是偶数数,n是奇数。 那么可以推断必定要翻转偶数次。 我们把两次归结为一次,则每次把0翻转成1的个数为 [0 , min(2n , 2m-2n)] 区间中的偶数。 以楼主m = 6, n = 5的例子来说,则可以翻转0个,翻转2个。

0 0 0 0 0 0
1 1 1 1 1 0
1 0 0 0 0 1
又因为要取最小翻转次数,那么翻转0个就相当于做无用功。所以这个例子每次只能翻转2,最后次数为6 / 2 * 2 = 6 次。 2. m偶数, n偶数。 那么既可以翻转偶数次,又可以翻转奇数次。 同上面一样,把两次当一次考虑,翻转0的个数取[0 , min(2n , 2m-2n)] 中的偶数。 那么对于例子 m = 6, n = 4。则可取0, 2, 4。 假如翻转偶数次, 那么可以取上面的2, 4 ,最少翻转次数为 2 * 2 = 4; 假如翻转奇数次, 那么可知倒数第二次状态为(n, m - n)。这时只要考虑从(m, 0)状态到 (n, m-n)状态就行了。 也即相当于翻转偶数次,使m-n个 0变成 1 ,这又变成了上面那个问题。上面的例子则是(6, 0)....-> (4, 2) -> (0, 6),那么最终结果为2 + 1 = 3 < 4;如下所示

0 0 0 0 0 0
1 1 1 1 0 0
1 0 0 0 1 0
1 1 1 1 1 1
3. m奇数,n偶数。无解 4. m奇数,n奇数。必定要翻转奇数次。同上面一样,翻转状态应为(m ,0) ....->(n ,m - n) -> (0 ,m)。 只需要求解翻转m-n个0的最少次数加一就行了。 这是只个人的思路,不知道正确否。
这个没人看吗。。。新人第一次回帖被忽略了
nice_cxf 2014-08-08
  • 打赏
  • 举报
回复
错了,上边的逻辑还是有些问题当n>m-n:的时候,如果n为奇数,不能直接返回3,4 ,还要仔细考虑下
nice_cxf 2014-08-08
  • 打赏
  • 举报
回复
好象这样就ok了,跟FancyMouse的程序验证了下,没问题,基本完全数学解法 总结下: 假定x=m%n; y=m/n m为奇数,n为偶数无解 如果m能整除n,需要y次 如果m-n=1,需要m次 如果n<m-n: 如果n为偶数,返回y+1 如果(m-n*y)为偶数,返回y+2 如果(m-n*y)为奇数,返回y+1 如果n>m-n: 如果n为偶数,返回3 如果2*n-m为偶数,返回4 如果2*n-m为奇数,返回3 程序如下:

int f(int m, int n)
{
	std::vector<int> r(m+1, -1);
    r[0] = 0;
    std::queue<int> q;
    q.push(0);
    while(!q.empty() && r[m] < 0)
    {
        int t = q.front();
        q.pop();
        int start = t <= m-n ? 0 : n-m+t;
        int end = t >= n ? n : t;
        for(int i = start; i <= end; i++)
        {
            int j = t - i + (n-i);
            if(r[j] < 0)
            {
                r[j] = r[t] + 1;
                q.push(j);
            }
        }
    }
    return r[m];
}

int calc(int m,int n)
{
	if (m<n)
	{
		printf("input error \n");
		return -1;

	}
	if (m%2==1 && (0==n%2)) // m奇数,n偶数无解
	{
		printf("no sul \n");
		return -2;
	}
	if (0==m%n)
	{
//		printf("sul=%d \n",m/n);
		return m/n;
	}
	if ( m-n==1)
	{
//		printf("sul=%d \n",m);
		return m;
	}
	if (2*n <m) //n<m-n
	{
		int x = m%n;
		int y =m/n;
		if (0==n%2)
		{
//			printf("sul=%d \n",y+1);
			return y+1;

		}
		if (0== (m-n*y)%2)
		{
//			printf("sul=%d \n",y+2);
			return y+2;
		}
		else
		{
//			printf("sul=%d \n",y+1);
			return y+1;

		}

	}
	else
	{
		if (0==n%2)
		{
//			printf("sul=3 \n");
			return 3;
		}
		int total = 2*n-m;
		if (0==total%2) 
		{
//			printf("sul=4 \n");
			return 4;
		}
		else
		{
//			printf("sul=3 \n");
			return 3;
		}
	}

}
 
int main()
{
	int m=0;
	int n=0;
	while (1)
	{
		printf("input m,n \n");
		scanf("%d,%d",&m,&n);
		if (0>=m || 0>=n )
			break;
		int ret1 =calc(m,n);
		int ret2 =f(m,n);
		printf("ret1=%d,ret2=%d \n",ret1,ret2);

	}
	return 0 ;
}

加载更多回复(44)

64,648

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧