一、约瑟夫环问题
约瑟夫环问题(Josephus)
用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。
问题描述1
N个人按顺时针围成一个圈,从1到N,然后报数,报到M的人就出去,然后剩余的人仍然围成一个圈,从出局的人下一个人开始重新报数,到M的人出局,如此循环。
【解法1】
建立一个有N个元素的循环链表,然后从链表表头遍历并记数,如果计数i==m(i初始为1)删除元素,依次类推,若当前元素等于该元素链接的下一元素时终止循环。
【解法2】
为了讨论方便,先把问题稍微改变一下,并不影响原意:
问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]
当i=1时,f[1]=0;
当i=2时,f[1]=m%2;
..........
递推公式
f[1]=0;
f[i]=(f[i-1]+m)%i; (i>1)
有了这个公式,我们要做的就是从1-n顺序算出f[i]的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1
由于是逐级递推,不需要保存每个f[i],程序也是非常简单:
#include<stdio.h>
int main()
{
int n,m,i,s = 0;
printf("N M =");
scanf("%d%d",&n,&m);
for(i = 2; i <= n; i++)
{
s = (s + m) % i;
}
printf("\n The winner is %d\n",s+1);
}
这个算法的时间复杂度为O(n),空间复杂度为常数。
问题描述2
唯一的不同之处就是每个人都会持有一个密码,这个密码用来设置M,也就说一个人出局以后,按他所拥有的密码作为M来进行淘汰。
可以像问题描述1一样,使用循环链表解决。在链表节点中多了一个密码成员即可。具体参见:
http://blog.csdn.net/jianzhibeihang/article/details/4952947
回文序列:
首先应该明白,一个序列正序和反序得到序列完全相同,这样的序列才是回文序列。具体有两种情况,奇数序列、偶数序列。如121,23432等是奇数回文;如1221,234432等就是偶数回文。
【解题思路1】
从一个字符串中找到一个位置,满足下面任一个条件:
(1)*p == *q; (p=pstr, q=pstr+1,pstr为字符指针位置)
(2)*p== *q (p=pstr, q=pstr+2,pstr为字符指针位置)
如果满足,则 p--,q++
这样既可以找到最大的回文序列。
【解题思路2】manacher算法
解题思路1中当找到一个回文后,在后面一段距离内仍需要进行检查是否是回文,有很多不必要的匹配。为了避免重复不必要的匹配,可以使用manacher算法,该算法时间复杂度O(n),专门用于解决字符串中求最大回文的问题。而且,manacher算法可以将奇数字符串和偶数字符串求回文问题统一成一个求奇数字符串回文问题。具体思路及代码在下面的链接中可以看到,这里不在赘述。http://blog.sina.com.cn/s/blog_70811e1a01014esn.html
三、大数相乘
大数用数组存放。模拟乘法计算过程,各位相乘后相加,并进行进位。
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #define N 100
- /*
- *将在数组中保存的字符串转成数字存到int数组中
- */
- void getdigits(int *a,char *s)
- {
- int i;
- char digit;
- int len = strlen(s);
- //对数组初始化
- for(i = 0; i < N; ++i)
- *(a + i) = 0;
- for(i = 0; i < len; ++i){
- digit = *(s + i);
- *(a + len - 1 - i) = digit - '0';//字符串s="12345",因此第一个字符应该存在int数组的最后一个位置
- }
- }
- /*
- *将数组a与数组b逐位相乘以后存入数组c
- */
- void multiply(int *a,int *b,int *c)
- {
- int i,j;
- //数组初始化
- for(i = 0; i < 2 * N; ++i)
- *(c + i) = 0;
- /*
- *数组a中的每位逐位与数组b相乘,并把结果存入数组c
- *比如,12345*12345,a中的5与12345逐位相乘
- *对于数组c:*(c+i+j)在i=0,j=0,1,2,3,4时存的是5与各位相乘的结果
- *而在i=1,j=0,1,2,3,4时不光会存4与各位相乘的结果,还会累加上上次相乘的结果.这一点是需要注意的!!!
- */
- for(i = 0; i < N; ++i)
- for(j = 0; j < N; ++j)
- *(c + i + j) += *(a + i) * *(b + j);
- /*
- *这里是移位、进位
- */
- for(i = 0; i < 2 * N - 1; ++i)
- {
- *(c + i + 1) += *(c + i)/10;//将十位上的数向前进位,并加上原来这个位上的数
- *(c + i) = *(c + i)%10;//将剩余的数存原来的位置上
- }
- }
- int main()
- {
- int a[N],b[N],c[2*N];
- char s1[N],s2[N];
- int j = 2*N-1;
- int i;
- printf("input the first number:");
- scanf("%s",s1);
- printf("/ninput the second number:");
- scanf("%s",s2);
- getdigits(a,s1);
- getdigits(b,s2);
- multiply(a,b,c);
- while(c[j] == 0)
- j--;
- for(i = j;i >= 0; --i)
- printf("%d",c[i]);
- printf("/n");
- return 0;
- }