经典算法——传染病问题(Java版)

某种传染病第一天只有一个患者,前5天为潜伏期,不发作也不会传染人,第6天开始发作,从发作到治愈需要5天时间,期间每天传染3个人,求第N天共有多少患者。
详细数据分析如下表:(前20天状况)
《经典算法——传染病问题(Java版)》
④潜伏期人数=前一天潜伏期人数-潜伏期转发作期人数+当天新传染人数
①潜伏期转发作期人数=5天前新感染人数
②发作期人数=前一天发作期人数-当天康复人数+5天前新感染人数(或当天潜伏转发作期人数[5~10天计算时只能使用这个量])
③当天新感染人数=3*当天发作期人数
②当天康复人数=5天前潜伏期转发作期人数(或10天前新感染人数)
⑤患者=潜伏期人数+发作期人数
根据上面的关系式可以简单分析出来,如果要求未知的某天的信息,就必须知道前一天的信息和前五天的信息
假设输入11天,则需要知道第10天和第6天的相关内容;假设输入13天则需要第12天和第8天内容,而第12天又需要知道第11天和第7天的数据信息
所以因为需要知道的距离输入时间最远的时间是5天,而未经过计算已知数据的最晚天数是第10天,则第11天到第15天是个转换阶段
下面是算法核心函数:
AC1(int days)用来计算对应天数的所有数据
AC2(int days)AC2用来反复调用AC1来实现对输入第N天得前面所有天数的数据求值的方式来求得第N天数据
ACattack(int days)计算当天发作人数
ACperpatent(int days)计算当天潜伏期人数
ACrecovery(int days)计算康复人数
ACQZF(int days)计算潜伏期转发作期的人数
ACinfection(int days)计算当天感染的人数
下面是算法的Java代码实现:

package com.cn;
import java.util.Scanner;

public class Patient2 {
    //下面数组用于寄存前99天的相关数据,[0]暂且不用,但是实际计算量太大这些天完全用不完就出现了溢出
	static int[] perpatent=new int[100]; //潜伏期人数
	static int[] QZF=new int[100];       //潜伏期转发作期人数
	static int[] attack=new int[100];    //发作期人数
	static int[] infection=new int[100]; //当天新感染人数
	static int[] recovery=new int[100];  //当天康复人数
	static int[] sickness=new int[100];  //患者人数

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int a=3;             //a和b没有实际意义只是为了给前十天赋值用的
		int b=4;
		for(int i=1;i<=5;i++){
			perpatent[i]=1;  //初始化前5天潜伏期人数都为1
			QZF[i]=0;        //初始化前5天潜伏期转发作期人数都为0
			attack[i]=0;     //初始化前5天发作期人数都为0
			infection[i]=0;  //初始化前5天当天新感染人数都为0
			recovery[i]=0;   //初始化前5天当天康复人数都为0
			sickness[i]=1;   //初始化前5天患者人数都为1
		}
		
		for(int i=6;i<=10;i++){		
			if(i==6){
				QZF[i]=1;    //初始化第6天潜伏期转发作期人数都为1
			}else
				QZF[i]=0;    //初始化第7天到第10天潜伏期转发作期人数都为0
			perpatent[i]=a;  //初始化第6天到第10天潜伏期人数每天+3
			a=a+3;
			attack[i]=1;     //初始化第6天到第10天发作期人数都为1
			infection[i]=3;  //初始化第6天到第10天当天新感染人数都为3
			recovery[i]=0;   //初始化第6天到第10天当天康复人数都为0
			sickness[i]=b;   //初始化第6天到第10天患者人数每天+3
			b=b+3;
		}		
		System.out.println("请输入天数:");
		Scanner scan=new Scanner(System.in);
		int days=scan.nextInt();
		PDays(days);		
	}
	
	//根据输入的天数来决定不同的计算范围
	public static void PDays(int days){
		if(days<=10){          //如果输入的天数小于十天则直接输出前十天的数组内容即可
			Print(days);
		}
		if(days>=11&&days<=15){//如果输入的天数大于10天且小于16天则调用计算函数AC1(days)
		    AC1(days);
		    Print(days);
		}
		if(days>=16){
			AC2(days);         //如果输入的天数大于15天则调用计算函数AC2(days)
			Print(days);
		}
	}
	//打印函数,用于输出最后的显示结果
	public static void Print(int days){
		System.out.println("第"+days+"天,潜伏期人数为:"+perpatent[days]);
		System.out.println("第"+days+"天,潜伏期转发作期人数为:"+QZF[days]);
		System.out.println("第"+days+"天,发作期人数为:"+attack[days]);
		System.out.println("第"+days+"天,当天新感染人数为:"+infection[days]);
		System.out.println("第"+days+"天,当天康复人数为:"+recovery[days]);
		System.out.println("第"+days+"天,患者人数为:"+sickness[days]);
	}
	
	//我们通过AC1可以计算出11~15天的每天的具体信息
	//注意AC1函数中的执行顺序不可混乱,需要按照上面序号由小到大的顺序先后获取,因为各个数据之间是有依赖关系的,有些数据依赖于其他数据已知才可求出
	public static void AC1(int days){
		QZF[days]=infection[days-5];                 //潜伏期转发作期人数=5天前新感染人数,而11~15天内的这个人数是已知的,于是可以直接传值过来
		recovery[days]=QZF[days-5];                  //当天康复人数=5天前潜伏期转发作期人数,而11~15天内的这个人数是已知的,于是可以直接传值过来
		attack[days]=ACattack(days);			     //发作期人数=前一天发作期人数-当天康复人数+5天前新感染人数,其中前一天发作期人数是未知的,我们通过函数来获取		                                          
		infection[days]=3*attack[days];              //当天新感染人数=3*当天发作期人数,上面已经求出来当天发作期人数,所以这个式子中都是已知量,传值即可
		perpatent[days]=ACperpatent(days);           //潜伏期人数=前一天潜伏期人数-潜伏期转发作期人数+当天新传染人数,其中前一天潜伏其人数是未知的,我们通过函数来获取
		sickness[days]=perpatent[days]+attack[days]; //患者=潜伏期人数+发作期人数,上面都已经计算出来需要的值,直接调用即可
	}
	
	//AC2用来反复调用AC1来实现对输入第N天得前面所有天数的数据求值的方式来求得第N天数据
	public static void AC2(int days){   //如果说AC1是核心函数,那么AC2是对AC1的充分利用,根据分析我们求任意一天的数据,那么之前每天的数据都必须求出来
		int y=days;                     //将输入的天数暂时寄存一下
		days=10;                        //前十天数据已知,则作为分界
		while(days<y){                  //因为前十天已知,而求任意一天的数据需要前面所有天数的数据都已知,那么从第11天开始我们挨个计算
			days=days+1;
			AC1(days);                  //反复调用AC1来计算第11~第N天的所有数据,我们必须这么做,因为第N天的数据依赖于第N-1天的数据,而第N-1天需要求出第N-2天数据,依次类推
		}
		return;
		
	}
	//计算当天发作人数(11~15天专用)
	public static int ACattack(int days){   
		if(days>11){                                                     //当天发作的人数=前一天发作期人数-当天康复人数+5天前新感染的人数
		attack[days]=ACattack(days-1)-ACrecovery(days)+infection[days-5];//前一天发作期人数未知则递归求,对于当天康复人数来讲,我们只通过AC1求出了输入当天的康复人数
		}else{                                                           //却没有求出来输入前一天的康复人数,则需要调用求康复人数的函数来求得				                                                 
			attack[days]=attack[days-1]-ACrecovery(days)+infection[days-5];//这行代码是核心,它是已知和未知的交界线,正是递归调用的已知点,转折点		
		}		
		return attack[days];
	}
	//计算当天潜伏期人数(11~15天专用)
	public static int ACperpatent(int days){       //潜伏期人数=前一天潜伏期人数-潜伏期转发作期人数+当天新传染人数;前一天潜伏期人数未知需要递归调用
		if(days>11){                               //潜伏转发作期人数和当天新感染人数此时只有输入当天的值是求出来过的,但是输入前一天或前N天都是未知的,则需要相应的求值函数
			perpatent[days]=ACperpatent(days-1)-ACQZF(days)+ACinfection(days);
		}else{
			perpatent[days]=perpatent[days-1]-ACQZF(days)+ACinfection(days);
		}
		return perpatent[days];
	}
	//下面这三个方法目的都是求相应天数的未知信息,但是基于11~15天,被调用的值多是已知量,也可以说下面这些方法是为上面两个核心数据关系服务的
	//计算康复人数(11~15天专用)
	public static int ACrecovery(int days){
		if(days>10){
			recovery[days]=QZF[days-5];
		}		
		return recovery[days];
	}
	//计算潜伏期转发作期的人数(11~15天专用)
	public static int ACQZF(int days){
		if(days>10){
			QZF[days]=infection[days-5];
		}		
		return QZF[days];
    }
	//计算当天感染的人数(11~15天专用)
	public static int ACinfection(int days){
		if(days>10){
			infection[days]=3*ACattack(days);
		}		
		return infection[days];
	}	
}

第一个关键在于疏通各个值之间的关系,先求最易求得的值,再求依赖关系复杂的值。第二个关键点在于需要发现若求第N天则需要知道前面所有N-1天的信息才可以(严格意义上讲需要求前N-1-10天的所有信息,因为前十天是已知的)。第三个关键在于需要通过数据来发现关系,发现前10天是个特殊期,11~15天是个转折期。

    原文作者:传染病问题
    原文地址: https://blog.csdn.net/qq_40324906/article/details/82952363
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞