zoukankan      html  css  js  c++  java
  • 约瑟夫环相关问题的解法

    约瑟夫环

    问题来源

           据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

    例如 10 个人,每报数到第3人自杀,求最后活下来的人。
    在这里插入图片描述
    所以最终活下的是第4位和第10位

    Java代码(链表):

    import java.util.*;
    
    public class YueSeFuHuan {
    	public static Integer[] live(int count, int k) {
    		// count 指人数, 每报数到第 k 人自杀
    		// livearr 存放最后活下来的人, 作为返回值。数组长度选取 k-1 和 count 较小的值即可
    		Integer livearr[] = new Integer[Math.min(count, k-1)];
    		// 如果 count 小于 k, 这 count 都可以存活
    		if(count < k) {
    			int index = 0;
    			while(index < count) 
    				livearr[index++] = index;
    			return livearr;
    		}
    		
    		// 使用链表,删除速度更快
    		LinkedList<Integer> people = new LinkedList<Integer>();
    		// 使用数组也可以(将上一行注释,下一行去注释即可)
    		// List<Integer> people = new ArrayList<Integer>();
    		
    		// 初始化链表
    		for(int i = 0;i < count;i++) 
    			people.add(i+1);    
    		// point 指向链表元素,  number 指报数
    		int point = 0, number = 0; 
    		// 只留下 k-1 个人
    		while(people.size() >= k) {
    			number++;
    			// 如果 point 到了链表末尾, 再返回指向链表开头(因为是环)
    			if(point >= people.size())
    				point = 0;
    			// 如果报数到第 k 人,则从链表中删除(自杀)
    			// 如果是 number == 3 , 需要把 number = 0 (重置0),此处取余可以避免重置0
    			if(number%k == 0) 
    				people.remove(point);
    			// 继续指向链表下一元素
    			else
    				point++;
    		}
    		// 将 people 转化为数组返回
    		return people.toArray(livearr);
    	}
    	
    	public static void main(String[] args) {
    		int count = 10, k = 3;
    		Integer [] livepeople = live(count, k);
    		for(int i = 0;i < livepeople.length;i++) 
    			System.out.print(livepeople[i] + "  ");
    	}
    
    }
    /*
     * Code Running Results
     * 4  10
     */
    

    Java代码(数组):

    import java.util.*;
    
    public class YueSeFuHuanTwo {
    	public static Integer[] live(int count, int k) {
    		// count 指人数, 每报数到第 k 人自杀
    		// livearr 存放最后活下来的人, 作为返回值。数组长度选取 k-1 和 count 较小的值即可
    		Integer livearr[] = new Integer[Math.min(count, k-1)];
    		// 如果 count 小于 k, 这 count 都可以存活
    		if(count < k) {
    			int index = 0;
    			while(index < count) 
    				livearr[index++] = index;
    			return livearr;
    		}
    		
    		 // 使用数组
    		 List<Integer> people = new ArrayList<Integer>();
    		// 初始化数组
    		for(int i = 0;i < count;i++) 
    			people.add(i+1);    
    		
    		// point 指向数组元素,  number 指报数
    		int point = 0, number = 0; 
    		// count-k+1 表示需要自杀人数
    		for(int i = 1;i <= count-k+1;) {
    			// 如果 point 到了数组末尾, 再返回指向数组开头(因为是环)
    			if(point >= people.size())
    				point = 0;
    			// 如果此人已自杀, 继续指向数组下一元素
    			if(people.get(point) < 0)
    				point++;
    			else { // 否则此人活着
    				number++;
    				// 如果报数到第 k 人,则从数组中设置为 -1 (代表自杀)
    				if(number%k == 0) {
    					// 用 -1 来标记自杀的人
    					people.set(point, -1);
    					i++;         // 自杀人数加一
    				// 否则继续指向数组下一元素
    				} else 
    					point++;     
    				
    			}
    		}
    		
    		int index = 0;
    		for(int i = 0;i < people.size();i++) 
    			// 存活的放在数组
    			if(people.get(i) > 0)
    				livearr[index++] = people.get(i);
    		
    		return livearr;
    	}
    	
    	public static void main(String[] args) {
    		int count = 10, k = 3;
    		Integer [] livepeople = live(count, k);
    		for(int i = 0;i < livepeople.length;i++) 
    			System.out.print(livepeople[i] + "  ");
    	}
    
    }
    /*
     * Code Running Results
     * 4  10
     */
    
    

    Java代码(队列):

    import java.util.*;
    
    public class YueSeFuHuanThree  {
    	public static Integer[] live(int count, int k) {
    		// count 指人数, 每报数到第 k 人自杀
    		// livearr 存放最后活下来的人, 作为返回值。数组长度选取 k-1 和 count 较小的值即可
    		Integer livearr[] = new Integer[Math.min(count, k-1)];
    		// 如果 count 小于 k, 这 count 都可以存活
    		if(count < k) {
    			int index = 0;
    			while(index < count) 
    				livearr[index++] = index;
    			return livearr;
    		}
    		// 使用队列
    		Queue<Integer> people = new LinkedList<Integer>();
    		// 初始化队列
    		for(int i = 0;i < count;i++) 
    			people.add(i+1);
    		
    		// number 指报数
    		int number = 0; 
    		// 只留下 k-1 个人
    		while(people.size() >= k) {
    			number++;
    			Integer item = people.remove(); // 出队
    			// 如果此人不需要自杀, 再入队
    			if(number%k != 0) 
    				people.add(item);
    		}
    		
    		int index = 0;
    		while(!people.isEmpty()) {
    			livearr[index++] = people.remove();
    		}
    		return livearr;
    	}
    	
    	public static void main(String[] args) {
    		int count = 10, k = 3;
    		Integer [] livepeople = live(count, k);
    		for(int i = 0;i < livepeople.length;i++) 
    			System.out.print(livepeople[i] + "  ");
    	}
    
    }
    /*
     * Code Running Results
     * 4  10
     */
    
    

    另一种问题,只剩下一个人。
    上面我们讲的是每到第K个删除,如果count大于等于k的话,最终会留下k-1个。现在无论多少个,最后只留下1个,就是说如果数量小于k个的时候我们继续循环删除,直到留下最后一个的为止。
    例如 n 个人,每报到 k 的人自杀(这里从 0 开始)
    在这里插入图片描述
    Java代码(递归):

    public class YueSeFuHuanFour {
    	public static int live(int n, int k) {
    		// 由于从 0 开始,所以结果加一
    	    return fun(n, k) + 1;
    	}
    	public static int fun(int n, int k) {
    		// 只有一个人时返回 0
    	    if (n == 1)
    	        return 0;
    	    return (fun(n - 1, k) + k) % n;
    	}
    	public static void main(String[] args) {
    		System.out.println(live(10, 3));
    	}
    
    }
    /* Code Running Results:
     * 4
     */
    

    Java代码(循环)

    public class YueSeFuHuanFive {
    	public static int live(int n, int k) {
    		// 只有一个人时返回 0
    		int result = 0;
    		// 从又二个人开始执行循环
    		for(int i = 2;i <= n;i++) 
    			result = (result+k) % i;
    		// 由于从 0 开始,所以结果加一
    		return result+1;  
    	}
    	public static void main(String[] args) {
    		System.out.println(live(10, 3));
    	}
    
    }
    /* Code Running Results:
     * 4
     */
    

    欢迎评论

  • 相关阅读:
    LibreOJ 6003. 「网络流 24 题」魔术球 贪心或者最小路径覆盖
    LibreOJ #6002. 「网络流 24 题」最小路径覆盖
    LibreOJ #6000. 「网络流 24 题」搭配飞行员 最大匹配
    LibreOJ 2003. 「SDOI2017」新生舞会 基础01分数规划 最大权匹配
    hdu 1011 Starship Troopers 树形背包dp
    Codeforces Round #135 (Div. 2) D. Choosing Capital for Treeland dfs
    hdu 6199 gems gems gems dp
    hdu 5212 Code 筛法或者莫比乌斯
    hdu 3208 Integer’s Power 筛法
    hdu 5120 Intersection 两个圆的面积交
  • 原文地址:https://www.cnblogs.com/jiaohuadehulike/p/14295002.html
Copyright © 2011-2022 走看看