一:首先科普一下约瑟夫问题的数学方法
(1) 不管是用list实现还是用vector实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比較烦,并且时间复杂度高达O(nm),当n,m很大(比如上百万,上千万)的时候,差点儿是没有办法在短时间内出结果的。我们注意到原问题不过要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此假设要追求效率,就要打破常规,实施一点数学策略。
(2) 为了讨论方便,先把问题略微改变一下,并不影响原意: 问题描写叙述:n个人(编号0~(n-1)),从0開始报数,报到(m-1)的退出,剩下的人继续从0開始报数。求胜利者的编号。 我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人開始): k k+1 k+2 ... n-2, n-1, 0, 1, 2, ... k-2 而且从k開始报0。 如今我们把他们的编号做一下转换: k --> 0
k+1 --> 1 k+2 --> 2 ... ... k-2 --> n-2 k-1 --> n-1 变换后就完全然全成为了(n-1)个人报数的子问题。
(3)假如我们知道这个子问题的解:比如x是终于的胜利者,那么依据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!变回去的公式非常easy,相信大家都能够推出来:x'=(x+k)%n 怎样知道(n-1)个人报数的问题的解?对,仅仅要知道(n-2)个人的解即可了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,以下写递推公式:
令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n] ;递推公式 ; f[1]=0; f[i]=(f[i-1]+m)%i; (i>1)
有了这个公式,我们要做的就是从1-n顺序算出f[i]的数值,最后结果是f[n]。由于实际生活中编号总是从1開始,我们输出f[n]+1 由于是逐级递推,不须要保存每一个f[i],程序也是异常简单。
二:原题再现:
(1)The Joseph's problem is notoriously known. For those who are not familiar with the original problem: from among n people, numbered 1, 2, . . ., n, standing in circle every mth is
going to be executed and only the life of the last remaining person will be saved. Joseph was smart enough to choose the position of the last remaining person, thus saving his life to give us the message about the incident. For example when n = 6 and m = 5
then the people will be executed in the order 5, 4, 6, 2, 3 and 1 will be saved.
Suppose that there are k good guys and k bad guys. In the circle the first k are good guys and the last k bad guys. You have to determine such minimal m that all the bad guys
will be executed before the first good guy.
(2)分析:本题是约瑟夫环变形 先引入Joseph递推公式,设有n个人(0,...,n-1),数m,则第i轮出局的人为f(i)=(f(i-1)+m-1)%(n-i+1),f(0)=0; f(i) 表示当前子序列中要退出的那个人(当前序列编号为0~(n-i));
拿个样例说:K=4,M=30;
f(0)=0;
f(1)=(f(0)+30-1)%8=5; 序列(0,1,2,3,4,5,6,7)中的5
f(2)=(f(1)+30-1)%7=6; 序列(0,1,2,3,4,6,7)中的7
f(3)=(f(2)+30-1)%6=5; 序列(0,1,2,3,4,6)中的6
f(4)=(f(3)+30-1)%5=4; 序列(0,1,2,3,4)中的4
........
根据题意,前K个退出的人必然是后K个人,所以仅仅要前k轮中仅仅要有一次f(i)<k则此m不符合题意。
接下来说说m的取值范围:我们考察一下仅仅剩下k+1个人时候情况,即坏人另一个未被处决,那么在这一轮中结束位置必然在最后一个坏人,那么開始位置在哪呢?这就须要找K+2个人的结束位置,然而K+2个人的结束位置必然是第K+2个人或者第K+1个人,这样就出现两种顺序情况:GGGG.....GGGXB 或 GGGG......GGGBX (X表示有K+2个人的那一轮退出的人)所以有K+1个人的那一轮的開始位置有两种可能即第一个位置或K+1的那个位置,限定m有两种可能:t(k+1) 或 t(k+1)+1; t>=1;
若遍历每个m必然超时,避免超时则须要打表和限制m的范围。
(3) 代码例如以下:
using namespace std;
const int MAX_SIZE = 14; int main(void)
{
int joseph[MAX_SIZE]={0}; //打表,保存各个k值相应的m值 int k;
int i;
while(cin>>k)
{
if(0 == k)
break;
if(joseph[k])// 此k值已经求过直接打印,并下一次循环
{
cout<<joseph[k]<<endl;
continue;
} int n=2*k; //总人数
int ans[30]={0}; //第i轮杀掉 相应当前轮的编号为ans[i]的人
//PS:每一轮都以报数为“1”的人開始又一次编号
// 比如ans[1] = 3 表示第一次把下标为3的人杀掉(事实上是第四个人)
int m=k+1; //所求的最少的报数,从k+1開始,由于
for(i=1;i<=k;i++)
{
ans[i] = (ans[i-1]+m-1)%(n-i+1);
//cout << ans[i] << ",";// 杀人的原始数组的下标,也相当于erase过程,由于要迁移
//假设应用vector的vec[i],vec.erase(iter)就能够实现杀人顺序
if(ans[i] < k)// 不符合杀人条件
{
i = 0;
m++;// m加一,从i=1再次杀人
}
}
joseph[k] = m;
cout << joseph[k] << endl;
}
return 0;
}//poj 1012
三:问题延伸:(更简单的问题)
接下来每行输入一个小孩的名字(人名不超过15个字符)
最后一行输入W,S (W < N),用逗号","间隔
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAX_SIZE = 20; int main()
{
int n,w,s;
int i,cp;
char str[MAX_SIZE];
vector<string> vec;
scanf("%d",&n);
for(i=0;i<n;i++)
{
cin >> str;
vec.push_back(str);
}
scanf("%d,%d",&w,&s);
vector<string>::iterator iter;
cp = 1;
for(iter=vec.begin()+w-1; vec.begin()!=vec.end();)
{
cp++;
if(iter == vec.end())
iter = vec.begin();
if(cp<=s)
{
iter++;
}
else
{
cout << *iter << endl;
iter = vec.erase(iter);
//iter++;// 由于删除iter,自己主动指向下一个元素的;
cp = 1;
}
} return 0;
}//poj 3750
*/ #include <iostream>
#include <cstdio>
#include <cstring>
#include <list>
using namespace std;
const int MAX_SIZE = 20; int main()
{
int n,w,s;
int i,cp;
char str[MAX_SIZE];
list<string> lists;
scanf("%d",&n);
for(i=0;i<n;i++)
{
cin >> str;
lists.push_back(str);
}
scanf("%d,%d",&w,&s);
list<string>::iterator iter;
iter = lists.begin();
while(w>1)
{
iter++;
w--;
}
cp = 1;
for(; lists.size()!=0;)
{
cp++;
if(iter == lists.end())
iter = lists.begin();
if(cp<=s)
{
iter++;
}
else
{
cout << *iter << endl;
iter = lists.erase(iter);// 由于删除iter,自己主动返回下一个元素的,vector也是一样;否则ter成为野指针
cp = 1;
}
}
return 0;
}//poj 3750
华为机试 之 joseph环的更多相关文章
-
2014华为机试西安地区B组试题
2014华为机试西安地区B组试题 题目一.亮着点灯的盏数 一条长廊里依次装有n(1≤n≤65535)盏电灯,从头到尾编号1.2.3.-n-1.n.每盏电灯由一个拉线开关控制.開始,电灯所有关着. 有n ...
-
华为机试001:字符串最后一个单词的长度(华为OJ001)
华为机试 字符串最后一个单词的长度 计算字符串最后一个单词的长度,单词以空格隔开. 提交网址: http://www.nowcoder.com/practice/8c949ea5f36f422594b ...
-
2014华为机试西安地区A组试题
2014华为机试西安地区A组试题 题目一.分苹果 M个同样苹果放到N个同样篮子里有多少种放法,同意有篮子不放. 1<=M<=10.1<=N<=10 比如5个苹果三个篮子,3,1 ...
-
华为机试ACM(字符组合问题)
今晚做了华为的机试,3道ACM题,最后一道是实现从M个不同字符中任取N个字符的所有组合. eg: input:ABC 2 output:AB AC BC 第一个输入为字符串,第二个输入为组合的字符个数 ...
-
华为机试正式版(西安c/c++/java),今天下午去机试的题目,新奇出炉了!
下面题目都是回顾的.题目都非常easy, 大家有些基础就能够參加!(语言能够是c/c++.也能够是java的) 题目一(60分): 字符串操作. 将小写转换成大写, 将大写转化为小写, 数字的不做转换 ...
-
华为机试_字符串识别_Vector的使用;
第一题:拼音转数字输入是一个只包含拼音的字符串,请输出对应的数字序列.转换关系如下:描述: 拼音 yi er san si wu liu qi ba jiu ...
-
输入一个字符串,去掉重复的字符,并按ASCII值排序-华为机试
import java.util.Scanner; //输入字符串,去掉重复的字符,并按ASSIC码值排序 public class quChong { public static void main ...
-
2015华为机试——数字基root
题目描写叙述: 求整数的Root:给定正整数,求每位数字之和;假设和不是一位数,则反复; 输入:输入随意一个或多个整数 输出:输出各位数字之和,直到和为个位数为止(输入异常,则返回-1),多行,每行相 ...
-
华为机试-iNOC产品部-杨辉三角的变形
题目描述 1 1 1 1 1 2 3 2 1 1 3 6 7 6 3 11 4 10 16 19 16 10 4 1以上三角形的数阵,第一行只有一个数1,以下每行的每个数,是恰好是它上面的数,左上角数 ...
随机推荐
-
Android中Shape的使用
先看一下文档对Shape Drawable的描述: Shape Drawable An XML file that defines a geometric shape, including color ...
-
[Node.js] Node + Redis 实现分布式Session方案
原文地址: http://www.moye.me/?p=565 Session是什么? Session 是面向连接的状态信息,是对 Http 无状态协议的补充. Session 怎么工作? Sessi ...
-
是否可以继承String类?
是否可以继承String类? String类是final类故不可以继承
-
【转载】STL";源码";剖析-重点知识总结
原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点 ...
-
Ajax简单实现文件异步上传的多种方法
1. 认识FormData对象 FormData是Html5新加进来的一个类,可以模拟表单数据 构造函数 FormData (optional HTMLFormElement form) (可选) 解 ...
-
NET系列文章
NET系列文章 由于博主今后一段时间可能会很忙(准备出书:<.NET框架设计—模式.配置.工具>,外加换了新工作),所以博客会很少更新: 在最近一年左右时间里,博主各种.NET技术类型的文 ...
-
python_16_序列化
如何实现不同编程语言进行交互? json数据,相当于语言中间的沟通桥梁 什么是json数据? imoprt json json.dumps(内容) --把内容转换 ...
-
ubuntu18.04搭建 kubernetes(k8s)集群
下面使用kubeadm来创建k8s cluster1. 所有主机节点上都需要安装docker # sudo apt-get update # sudo apt-get install \ apt-tr ...
-
hihocoder1490 Tree Restoration 模拟
There is a tree of N nodes which are numbered from 1 to N. Unfortunately, its edges are missing so w ...
- [UE4]用Blenspace混合空间动画代替AimOffset动画偏移