用模拟法解决问题的基本思想是对事物进行抽象,将现实世界的事物映射成计算机所能识别的代码符号,将现实事物之间的关系映射成运算或逻辑控制流。
目录
模拟法对于程序设计来说,就类似十位数的加减乘除对于数学运算,又比如学习语言时的拼音和字母。我们通过模拟以及更进一步的抽象,可以建立起连接现实世界和计算机的代码世界的桥梁。
(1)习惯模拟的思考方式,学会对事物进行抽象的处理,将动态的流程映射为抽象的代码
(2)养成良好的代码编写风格和编程习惯。
(3)熟悉最基本的程序结构,对我们来说,这是后续学习的基础和基本功,对程序来说,是复杂程序的基本单元。
难度依次递减:鸡兔同笼——>校门外的树——>约瑟夫问题——>装箱子问题——》排列【POJ 1833】
鸡兔同笼
今有兽,六首四足;禽,四首二足,下有四十六足。问:至多几只?至少?
N位脚数,用模拟法将问题抽象出为脚数是奇数还是偶数、能否被4整除
难度:♥
(1)、若N是奇数,则说明没有满足条件的答案
(2)、若N是偶数且能被4整除,则最少有N/4只兔子,最多N/2只鸡
(3)、若N是偶数且不能被4整除,则最少有N/4只兔子和一只鸡,最多有N/2只鸡
package com.suanfa;
import java.util.Scanner;
public class ChickAndRabbit
{
public static void main(String[] args)
{
Scanner scn = new Scanner(System.in); //从外设接受数据
System.out.println("输入你要测试鸡兔同笼问题的组数:");
int n = scn.nextInt();
for (int i = 0; i < n; i++)
{
System.out.print("输入你第"+ (i + 1) +"次看到的脚数量:");
int x = scn.nextInt();
if (x < 0)
System.out.println("输入的值错误");
else if (x % 4 == 0)
System.out.println("最多" + x / 2 + " 最少" + x / 4);
else if (x % 2 == 0)
System.out.println("最多" + x / 2 + " 最少" + (x / 4 + 1));
else
System.out.println("/0.-.0\\");
}
}
}
校门外的树
校门外的树:校门外长度为L的树的马路有一排树,每两棵相邻的树之间的间隔是1m,在路间修高铁,需要移走一些树,问题为完成修高铁后马路上还有多少树。
难度:♥♥
方法一:将树抽象出来为一个数组,每当树被砍掉就令数组对应位置的值变化,没被砍就为非0,砍了就为1。
方法二:暂无
方法三:暂无
package com.suanfa;
import java.util.Scanner;
public class SchoolTrees
{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int l=sc.nextInt();
int m=sc.nextInt();
int[] s=new int[l+1];
for(int i=0;i<=l;i++)
s[i]=0;
for(int i=0;i<m;i++)
{
int a = sc.nextInt();
int b = sc.nextInt();
for(int j=a;j<=b;j++)
s[j]++;
}
int sum=0;
for(int i=0;i<=l;i++)
if(s[i]==0)
{
sum++;
}
System.out.println(sum);
}
}
约瑟夫问题
约瑟夫问题(猴子选大王):约瑟夫问题也叫做约瑟夫环,是一个数学应用问题。
问题内容:已知有n个人(分别编号为1,2,3…n)围坐成一圈,从第一个人开始报数,报到数m的人出圈;再从下一个人开始重新报数,报到m的人出圈;直至剩下最后一个人的时候游戏结束。输出剩下的人的原始编号。
难度:♥♥
解题思路:这个题通过数学推导找到一个合适的解是十分困难的,因此用算法的思想来解决,用基本的思想就是模拟整个过程。将N个树排成一圈,从1开始数,然后数到对应的M就划掉,一直循坏下去,直到最后就只剩下一个数。
package com.suanfa;
import java.util.Arrays;
import java.util.Scanner;
public class Yuesefu
{
public static void main(String[] args)
{
Scanner sca = new Scanner(System.in);
System.out.println("输入猴子数量");
int n = sca.nextInt();
System.out.println("输入数到数字退圈的数字");
int m = sca.nextInt();
int[] a = new int[n]; //声明一个数组
Arrays.fill(a,0); //数组赋值为0
int count = 0; //记录退出去的人数
int s=0; //纪记录从0数到m
//退出的人数小于总人数继续进行,就算第一轮结束,只要人数不满足就继续第二轮循坏
while (count < n)
{
for (int i = 0; i < a.length; i++) //循坏判断
{
if (a[i] == 0) //当值为0就令s加一
{
s++;
if (s == m) //s等于m 则退出一个人,并使这个人赋值为1
{
a[i] = 1;
count++; //退出一个就加一个
s = 0; //刷新s的值,开始下一轮计数
}
}
if (count == n - 1) //当退出的人等于n-1停止循坏
break;
}
}
//遍历输出值为0的位置
for (int i=0; i<a.length; i++)
{
if(a[i] == 0)
System.out.println("\n剩下的是"+(i+1));
}
}
}
装箱子问题
装箱子问题:有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数)。要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
第一行为一个整数,表示箱子容量;第二行为一个整数,表示有n个物品;接下来n行,每行一个整数表示这n个物品的各自体积。
难度:♥♥♥♥
这个题,我刚拿到的时候看起来感觉很繁琐,不过带入一些数据理解这个分析过程还是很好理解的。
一个6X6的产品,装入一个6X6的箱子中就只剩0X0的空间^-^
一个5X5的产品,装入一个6X6的箱子中还有11个1X1的空间
一个4X4的产品,装入一个6X6的箱子中还剩5个2X2的空间
一个3X3的产品,装入一个6X6的箱子中还剩7个1X1的空间+5个2X2的空间
两个3X3的产品,装入一个6X6的箱子中还剩6个1X1的空间+3个2X2的空间
三个3X3的产品,装入一个6X6的箱子中还剩5个1X1的空间+1个2X2的空间
四个3X3的产品,装入一个6X6的箱子中还剩0个1X1的空间+0个2X2的空间
package com.suanfa;
import java.util.Scanner;
public class Box
{
public static void main(String[] args)
{
int n, a, b, c, d, e, f, y, x;//定义需要的变量
Scanner sca = new Scanner(System.in);
System.out.println("输入1x1的产品数:");
a = sca.nextInt();
System.out.println("输入2x2的产品数:");
b = sca.nextInt();
System.out.println("输入3x3的产品数:");
c = sca.nextInt();
System.out.println("输入4x4的产品数:");
d = sca.nextInt();
System.out.println("输入5x5的产品数:");
e = sca.nextInt();
System.out.println("输入6x6的产品数:");
f = sca.nextInt();
int[] u = {0, 5, 3, 1};//将两个箱子
n = f + e + d + (c + 3) / 4;
x = 5 * d + u[c % 4];
if (b > x)
n += (b - x + 8) / 9;
y = 36 * n - 36 * f - 25 * e - 16 * d - 9 * c - 4 * d;
if (a > y)
n += (a - y + 35) / 36;
System.out.println(n);
}
}
排列问题
问题描述:给出一个正整数n,则从1~n这n个数可以构成N!个排列,将这些排列按照从小到大的顺序列出,例,n=3时,排列有123,132,213,231,312,321这六种排列。
任务描述:给出某个排列,求这个排列下的k个排列,若遇到最后一个排列,则下一个是第一个排列,即使1 2 …… n。例如,n=3,k=2,给出排列321,下两个是132。
假如一组数,2341546.
2341546 k=0
2341564 k=1
2341645 k=2
2341654 k=3
2344156 k=4
2344165 k=5
………… ……
2346541 k=n
………… ……
2314456 k=m
………… ……
6544321 k=...
从上面的规律可知,从后面往前推,将后面的进行升序排列
1、从右往左找,找到某个位置i,满足i-1的位置的数比i位置的数小
2、将i-1位置的数,与右边所有的数对比,将比他大的中最小的数进行交换
(比i大的最小的数:4比3大,5比三大,6也比三大,但是4是比三大中最小的一个数)
3、将i~n之间的数进行升序排列
package com.suanfa;
import java.util.Arrays;
import java.util.Scanner;
public class PailieTwo
{
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
System.out.println("输入数组大小");
int n = scan.nextInt();
int[] arr = Sha(n);
System.out.println(Arrays.toString(arr));
System.out.println("第几个排列");
int k = scan.nextInt();
for (int i = 0; i < k; i++)
System.out.println("排列下"+(i+1)+"排列是"+ Arrays.toString(next(arr)));
}
private static int[] next(int[] arr)
{
int size = arr.length;
int flag = size - 1;
int tmp;
while (flag != 0 && arr[flag - 1]>arr[flag])
flag--;
if (flag == 0) {
for (int i = 0; i < size / 2; i++) {
tmp = arr[i];
arr[i] = arr[size - 1 - i];
arr[size - 1 - i] = tmp;
}
return arr;
}
for (int i = size - 1; i >= flag; i--) {
if (arr[i] > arr[flag - 1]) {
tmp = arr[i];
arr[i] = arr[flag - 1];
arr[flag - 1] = tmp;
break;
}
}
while (flag < size -1)
{
tmp = arr[flag];
arr[flag] = arr[size - 1];
arr[size - 1] = tmp;
flag++;
size--;
}
return arr;
}
private static int[] Sha(int n)//进行随机一个组合
{
int[] baseA = new int[n];//弄一个数组存储
for (int i = 0; i < n; i++)//将1~n的值赋入数组
baseA[i] = i + 1;
int[] tarA = new int[n];//接受打乱后的数组
int index;//记录下标
for (int i = 0; i < n; i++)
do {
index = (int) (Math.random() * n);//随机值从0~n-1
tarA[i] = baseA[index];//将随机到的值对应的值传入新数组
baseA[index] = -1;//将随机到的值为-1,下面进行判断,如果值是-1就跳过,进入下一个值
} while (tarA[i] == -1);
return tarA;
}
}