约瑟夫问题的一种解法

/*

* 问题原型

*/

41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。约瑟夫将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

/*

* 问题分析

*/

这个问题还有一种描述为丢手绢问题,因此代码命名以shoujuan

由于最近在学习java,因此决定用java链表来完成这个问题。设计一个循环链表,将41个人链接起来,选定一个人作为起始,逢3进行remove动作。

需要设计类:

  shoujuan,用以表示每个人。包含成员:number用以计数,nextJuan用以表示下一个人。

  ShouJuanList,用以表示链表。包含成员:shouJuanFirst用以表示第一个人,shouJuanNbr用以表示总人数。包含方法:makeCircle()制造环形链表,addShouJuan()新增一个节点,removeShouJuan()删除一个节点。

/*

* 难点分析

*/

在完成这个代码的过程中,主要考虑到循环链表的特性,即最后一个又指向头结点。完成这个代码需要对传值和传址有十分清晰的认识,传入的地址是会影响这个地址上是值的。

需要对头结点、第二个结点做特殊的考虑,防止有遗漏情况。

方法不是最简的,作为学习途中的一个标记,mark一下。

/*

* 代码

*/

ShouJuan.java

 1 package com.diushoujuan;
 2
 3 public class ShouJuan {
 4     private int number;
 5     private ShouJuan nextJuan = null;
 6
 7     public ShouJuan(int n){
 8         this.number = n;
 9     }
10
11     public int getNumber() {
12         return number;
13     }
14
15     public void setNumber(int number) {
16         this.number = number;
17     }
18
19     public ShouJuan getNextJuan() {
20         return nextJuan;
21     }
22
23     public void setNextJuan(ShouJuan nextJuan) {
24         this.nextJuan = nextJuan;
25     }
26
27     public void showNbr(){
28         System.out.println("nub is: "+ this.number);
29     }
30 }

ShouJuanList.java

 1 package com.diushoujuan;
 2
 3 /*
 4  * 创建循环链表
 5  */
 6 public class ShouJuanList {
 7     private ShouJuan shouJuanFirst = null;
 8
 9     private int shouJuanNbr = 0;
10
11     public int getShouJuanNbr() {
12         return shouJuanNbr;
13     }
14
15     public void setShouJuanNbr(int shouJuanNbr) {
16         this.shouJuanNbr = shouJuanNbr;
17     }
18
19     public ShouJuan getShouJuanFirst() {
20         return shouJuanFirst;
21     }
22
23     public void setShouJuanFirst(ShouJuan shouJuanFirst) {
24         this.shouJuanFirst = shouJuanFirst;
25     }
26
27     //制造环形链表
28     public void makeCircle(){
29         if(shouJuanFirst != null){
30             ShouJuan shouJuanTemp = shouJuanFirst;
31             while(null != shouJuanTemp.getNextJuan()){
32                 shouJuanTemp = shouJuanTemp.getNextJuan();
33             }
34             shouJuanTemp.setNextJuan(shouJuanFirst);
35         }
36     }
37
38     //新增一个节点
39     public void addShouJuan(ShouJuan shouJuan){
40         ShouJuan shouJuanTemp = null;
41         if(shouJuanFirst == null){
42             shouJuanFirst = shouJuan;
43         }else{
44             shouJuanTemp = shouJuanFirst;
45             while(null != shouJuanTemp.getNextJuan()){
46                 shouJuanTemp = shouJuanTemp.getNextJuan();
47             }
48             shouJuanTemp.setNextJuan(shouJuan);
49         }
50         shouJuanNbr++;
51     }
52
53     //删除一个节点
54     public void removeShouJuan(ShouJuan shouJuan){
55         ShouJuan shouJuanTemp = shouJuanFirst;
56         if (shouJuanTemp == shouJuanFirst && shouJuanTemp.getNumber() == shouJuan.getNumber() && shouJuanNbr == 1) {
57             shouJuanFirst = null;
58             shouJuanNbr--;
59         }else if(shouJuanTemp == shouJuanFirst && shouJuanTemp.getNumber() == shouJuan.getNumber() && shouJuanNbr == 2){
60             shouJuanFirst = shouJuanFirst.getNextJuan();
61             shouJuanNbr--;
62         }else if(shouJuanTemp == shouJuanFirst && shouJuanTemp.getNumber() == shouJuan.getNumber() && shouJuanNbr>2){
63             while(shouJuanTemp.getNextJuan() != shouJuanFirst){
64                 shouJuanTemp = shouJuanTemp.getNextJuan();
65             }
66             shouJuanTemp.setNextJuan(shouJuanFirst.getNextJuan());
67             shouJuanFirst = shouJuanFirst.getNextJuan();
68             shouJuanNbr--;
69         }else{
70             if(shouJuanTemp == shouJuanFirst && shouJuanTemp.getNumber() != shouJuan.getNumber()){
71                 shouJuanTemp = shouJuanTemp.getNextJuan();
72             }
73             while(true){
74                 if(shouJuanTemp.getNextJuan().getNumber() == shouJuan.getNumber()){
75                     shouJuanTemp.setNextJuan(shouJuanTemp.getNextJuan().getNextJuan());
76                     shouJuanNbr--;
77                     break;
78                 }
79                 shouJuanTemp = shouJuanTemp.getNextJuan();
80             }
81         }
82     }
83 }

PlayGame.java

package com.diushoujuan;

public class PlayGame {

    public final static int shouJuanNbr = 2;
    public final static int shouJuanRemoveNbr = 2;

    public static void main(String[] args) {
        ShouJuanList shouJuanList = new ShouJuanList();

        for(int i = 1; i<=shouJuanNbr; i++){
            ShouJuan shouJuan = new ShouJuan(i);
            shouJuanList.addShouJuan(shouJuan);
        }
        shouJuanList.makeCircle();

        ShouJuan shouJuanRemove = shouJuanList.getShouJuanFirst();
        while(shouJuanList.getShouJuanNbr() >= shouJuanRemoveNbr){
            if (shouJuanRemove != shouJuanList.getShouJuanFirst()) {
                shouJuanRemove= shouJuanRemove.getNextJuan();
            }
            for(int i = 1;i< shouJuanRemoveNbr;i++){
                shouJuanRemove = shouJuanRemove.getNextJuan();
            }
            shouJuanList.removeShouJuan(shouJuanRemove);
        }

        ShouJuan shouJuanShow = shouJuanList.getShouJuanFirst();
        for(int i = 1;i< shouJuanRemoveNbr;i++){
            shouJuanShow.showNbr();
            shouJuanShow = shouJuanShow.getNextJuan();
        }
    }

}
时间: 2024-10-11 22:29:08

约瑟夫问题的一种解法的相关文章

有关经典约瑟夫问题的几种解法

约瑟夫问题是信息学奥赛中的一类经典且重要的题型,在平常的测试中屡屡出现. 通常题设可抽象为:一开始有 $n $个人围成一个圈, 从 $1 $开始顺时针报数, 报出 $m $的人被踢出游戏..然后下一个人再从$ 1 $开始报数,直到只剩下一个人.     或者:曾经有个人在他身边,然而现在只剩他一个人.$Who$  $are$  $you$$?$  $Who$ $am$ $I$$?$  $Why$ $am$ $I$ $here$$? $走的越来越慢,人越来越少,可终于还是只剩一个了呢.他们围成一圈

]Leetcode]-[Reorder List ]-三种解法

Given a singly linked list L: L0→L1→…→Ln-1→Ln,reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do this in-place without altering the nodes' values. For example,Given {1,2,3,4}, reorder it to {1,4,2,3}. 题目的意思就是,给定一个链表,从两头开始链接, 比如1-2-3-4-5-6,最开始取两头,组成1-

hdu 4521 小明系列问题——小明序列 (间隔至少为d的LIS 两种解法)

先附上资源地址:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html 进程(process)和线程(thread)是操作系统的基本概念,但是它们比较抽象,不容易掌握. 最近,我读到一篇材料,发现有一个很好的类比,可以把它们解释地清晰易懂. 1. 计算机的核心是CPU,它承担了所有的计算任务.它就像一座工厂,时刻在运行. 2. 假定工厂的电力有限,一次只能供给一个车间使用.也就是说,一个车间开工的时候,其他车间都必须停工

UVALive 6257 Chemist&#39;s vows --一道题的三种解法(模拟,DFS,DP)

题意:给一个元素周期表的元素符号(114种),再给一个串,问这个串能否有这些元素符号组成(全为小写). 解法1:动态规划 定义:dp[i]表示到 i 这个字符为止,能否有元素周期表里的符号构成. 则有转移方程:dp[i] = (dp[i-1]&&f(i-1,1)) || (dp[i-2]&&f(i-2,2))     f(i,k):表示从i开始填入k个字符,这k个字符在不在元素周期表中.  dp[0] = 1 代码: //109ms 0KB #include <ios

正整数划分的另一种解法 (纯递归)

Step 1: n ==1 : return 1 n == 2 : return  [1,1],[2] Step 2:for n > 2a.arr.push(n)b.arr.push([n-1,1]) c.1 get result of recursion(n-2)c.2 combine n==2 & result => retc.3 remove duplicate record in ret code : var splitN = function f(n){ if(n == 1)

*HDU 1394 经典逆序数的四种解法

1.暴力 [代码]: 1 /*HDU1394暴力写法*/ 2 #include <iostream> 3 #include <string.h> 4 #include <stdio.h> 5 6 using namespace std; 7 8 int A[50005]; 9 int Low[50005],Up[50005]; 10 int main(){ 11 int n; 12 while(~scanf("%d",&n)){ 13 int

最长单调递增子序列的三种解法

问题描述: 找出由n个数组成的序列的最长单调递增子序列 解法一:转化成LCS问题求解,时间复杂度为O(n*n). 思路:原序列为A,把A按升序排序得到序列B,求出A,B序列的最长公共子序列,即为A的最长单调递增子序列. #include<iostream> #include<algorithm> #include<string> #include<cstdio> using namespace std; //转化成LCS问题,时间复杂度O(n*n) int

Gas Station [leetcode] 的两种解法

由于gas总量大于cost总量时,一定可以绕所有城市一圈. 第一种解法: 假设一开始有足够的油,从位置i出发,到位置k时剩余的油量为L(i,k). 对任意的k,L(i,k)根据i的不同,只相差常数. 我们只需要找到最小的L(0, k)对应的k,k+1为所求. 代码如下: int canCompleteCircuit(vector<int> &gas, vector<int> &cost) { int start = 0; int curGas = 0, minGas

POJ 1515 Street Directions --一道连通题的双连通和强连通两种解法

题意:将一个无向图中的双向边改成单向边使图强连通,问最多能改多少条边,输出改造后的图. 分析: 1.双连通做法: 双连通图转强连通图的算法:对双连通图进行dfs,在搜索的过程中就能按照搜索的方向给所有边定向,其中桥不能改造,只能保留双向边. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #includ