关于动态规划的一篇blog(约瑟夫环问题)

题目:n个玩家围成一个圈,从第一个玩家开始报数,报到m的人被枪毙,下一个人接着从1开始报数报到m的人被枪毙,下一个人接着从1开始报数,直到最后一个活下来的人获得胜利。已知参与的玩家数量为n,被枪毙的数是m,要选到几号位置才会活下来,(1<m<n<100)

设计一个程序,输入n和m,输出最后的胜利者

样例输入:10 3

样例输出:4

暴力解法

直接根据题目要求按部就班的求,不多说了直接上代码

import java.util.Arrays;
import java.util.Scanner;
public class Text5_03 {
    public static void main(String[] args) {
        //创建Scanner对象
        Scanner sc = new Scanner(System.in);
        //提示用户输入 并接收n,m
        System.out.print("n = ");
        int n = sc.nextInt();
        System.out.print("m = ");
        int m = sc.nextInt();
        // alive表示对应编号参与者是否被枪毙 并赋初值为1
        // 1 表示未被枪毙
        // 0 表示已被枪毙
        int [] alive = new int[n];
        Arrays.fill(alive,1);
        //cor%n用于表示当前循环到编号为几的参与者 利用取模运算作循环数组
        int cor = 0;
        //aliveNum表示剩余人数 当剩余人数为1时停止循环
        int aliveNum = n;
        while(aliveNum > 1){
            //向后报数
            int key = m;
            while(key > 0){
                //if语句判断是否为活人 活人才能报数
                if(alive[cor%n] == 1){
                    key--;
                }
                cor++;
            }
            //令其alive值为0表示被枪毙 并将剩余人数减一
            //最后一次cor多加了一次
            alive[(cor-1)%n] = 0;
            aliveNum--;
        }
        //循环找到胜利者
        while(alive[cor%n] != 1){
            cor++;
        }
        //打印胜利者编号
        System.out.println(cor%n + 1);
        //关闭Scanner对象
        sc.close();
    }
}

动态规划

我们来想一下,假如有11个人,每4个人杀一个,第9个人会活下来

假如有10个人,每4个人杀一个,第5个人会活下来

假如有9个人,每4个人杀一个,第1个人会活下来

好像有某种规律。

不妨这样考虑问题,以下考虑的情形都是每m个人杀一个,且m<n。

已知:现在有n个人,第x个人活下来了(称其为A吧)

求解:如果现在有n+1个人。现在第一轮死掉了一个人,但是这个人不是A。

剩下n个人接着玩这个游戏,最后A活下来了,那么A应该是第几个人呢?

接着玩这个游戏的时候,第一个报1的人,其实是最开始第m+1位置上的人

也就是如果把剩下n个人的序号都往前推m个(前m个人就去队尾了)就和已知是一个问题了

“第一个人”A
n个人玩的时候的序号1x
n+1个人玩的时候的序号m+1

因为众人绕成环,是连续的,所以可以推出此时A的序号是x+m

并且由于x+m可能会大于n,所以严谨来讲A的序号应该为 (x+m)%n

有递推公式 f(N,M)=(f(N−1,M)+M)%N

在数组中index从0开始 最后要记得加一。

后话:实际操作时index从0开始更好,假如有5个人,编号是0~4,取模5之后的结果也是0~4

但是如果index从1开始取,这五个人的编号是1~5,取模5之后结果是0~4会出现问题

import java.util.Scanner;
​
public class Text5_03NEW {
    public static void main(String[] args) {
        //创建Scanner对象
        Scanner sc = new Scanner(System.in);
        //提示用户进行输入 n,m
        System.out.print("Enter n = ");
        int n = sc.nextInt();
        System.out.print("Enter m = ");
        int m = sc.nextInt();
        //约瑟夫环问题 属于动态规划问题
        //可得出递推公式: f(N,M)=(f(N−1,M)+M)%N
        int[] alive = new int[n+1];
        alive[1] = 0;
        for (int i = 2; i <= n; i++) {
            alive[i] = (alive[i - 1] + m) % i;
        }
        //打印胜利者编号
        System.out.println(alive[n] + 1);
        //关闭Scanner对象
        sc.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值