hdu -- 1443 Joseph

本文详细解析了约瑟夫问题及其算法实现,介绍了如何通过编程解决这一经典问题,并利用打表法提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


   在hdu中,这道题要先杀掉后排才能杀掉前排,且杀掉一个人后,重新回环,即题目为杀掉后排之前不得杀掉前排,于是如下:
   1.将死之人为 die = m % i == 0 ?i :m % i;(i是剩余的人数)
   2.不可杀的范围:!(a[die] =< k && a[die] >= 1)不为前k个人(前面给数组赋了初值)

   3.移位

借助a[ 0 ]进行移位,先将最后一位移给a[ 0 ],再将数组整体后移移位,直到a [ 0 ] ==  b ; (b == a[die]).


   代码如下 

#include<stdio.h>
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	int a[25],k,m,die,kill;
	while(scanf("%d",&k) != EOF)
	{
		for(m = k + 1;;m++)
		{
			//给数组赋初值
			for(int i = 1; i <= 2*k; i++)
				a[i] = i;
			kill = 0; //已杀人数
			for(int i = 2 * k;;i--)
			{
				die = m % i == 0 ? i : m % i;
				if(!(a[die] >= 1 && a[die] <= k)) //条件判断
				{
					int b = a[die];
					kill++;
					a[0] = a[i]; 
					while(a[0] != b)  //移位
					{
						for(int j = i; j > 0; j--)
							a[j] = a[j - 1];
						a[0] = a[i];
					}
				}
				else  break;
			}
			if(kill == k) { printf("%d,",m); break;}		
		}

	}
	return 0;
}

   这时打出的代码在k = 14 时候,将近需要11s,这严重超时啊有木有~~~~~

   这时考虑到约瑟夫问题数据少,所以可以用打表法来过题,所谓打表法,先写代码将数据打出,然后初始化于数组中,这里推荐使用输入流和输出流,我使用了freopen(“input.txt”,“r”,stdin);

freopen(“output.txt”,“w”,stdout);将这两代码,写于main函数入口处,之后的scanf只会从文件input.txt读入数据,所以要自己新建,一般建于与cpp相同的目录下,也可以建于其它地方,但要写绝对路径或相对路径(默认是与cpp相同的目录),例如:freopen(“D://hdu/joseph/input.txt”,"r",stdin);你的printf将把数据写于output.txt中(不必自己新建),这样,你就可以简单的将数据复制给数组打表了。

    打表代码:

#include<stdio.h>
int a[14] = {2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881,13482720};
int main()
{
    int b;
    while(scanf("%d",&b) == 1 && b != 0)
    {
        printf("%d\n",a[b-1]);
    }
    return 0;
}


### 关于HDU-1443问题的Java实现 针对HDU平台上的题目ID为1054的问题,在64位整数输入输出格式方面应采用`%lld`,而Java类名需指定为主类名为`Main`[^1]。 对于解决该问题的一个实例是在处理两个数组中寻找重复数字的任务时。在Java方法内部可以利用`duplication[0]`来替代C/C++里的指针形式`*duplication`,这使得可以在返回布尔值的同时获取所需的数值[^2]。 下面给出一段适用于此场景下的Java代码片段: ```java import java.util.HashSet; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); HashSet<Integer> set = new HashSet<>(); while (scanner.hasNextInt()) { int num = scanner.nextInt(); if (!set.add(num)) { // 如果add失败,则说明已经存在这个元素 System.out.println("Duplicate number found: " + num); break; // 或者继续收集所有的重复项取决于具体需求 } } scanner.close(); } private static boolean duplicate(int numbers[], int length, int duplication[]) { if (numbers == null || length <= 0) return false; for (int i = 0; i < length; ++i) { while (numbers[i] != i) { if (numbers[i] == numbers[numbers[i]]) { duplication[0] = numbers[i]; return true; } else { // Swap elements to their correct positions. int temp = numbers[numbers[i]]; numbers[numbers[i]] = numbers[i]; numbers[i] = temp; } } } return false; } } ``` 这段程序首先定义了一个`HashSet`用于存储读取到的数据,并通过尝试向集合中添加新元素的方式来检测是否存在重复数据;一旦发现有重复即刻打印并停止进一步操作。另外还提供了一个辅助函数`duplicate()`用来判断给定数组内是否有任何重复元素,并将找到的第一个重复值存入传入参数`duplication[]`的第一位置上以便调用方访问。 需要注意的是上述代码仅作为示例展示如何运用特定技巧解决问题,并不一定完全符合原题目的所有约束条件以及边界情况处理,请根据实际题目描述调整逻辑细节以满足要求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值