武汉理工大学计算机考研历年复试真题实现

2019年算法真题

No.1 凑硬币

【题目】:

现在有硬币1分,5分,10分,25分。我现在想凑齐63分的硬币,请问如何用最少的硬币凑成63分呢。比如这道题答案应是25,25,10,1,1,1

思想:采用贪心法,每次选取超过当前money的最大的硬币值,然后计算每次需要的不同硬币的个数

public static void main(String[] args) {
    int[] coin = {1,5,10,25};
    int money=63;
    chooseCoin(coin,money);

}
public static void chooseCoin(int[] coin,int money){
    int restMoney = money;
    int i;
    for(i=coin.length-1;i>=0;i--){
        int countCurrent=0;
        //每次选取不超过restMoney的最大的硬币值,计算所需要的硬币数
        if(restMoney>=coin[i]){
            countCurrent=restMoney/coin[i];
            restMoney=restMoney%coin[i];
            System.out.println("需要"+coin[i]+"分的硬币"+countCurrent+"个");
        }
    }
}

No.2 括号匹配

【题目】:

给定一个只包括 (){}[] 的字符串,判断字符串是否有效。
有效字符串需满足:
1、左括号必须用相同类型的右括号闭合。
2、左括号必须以正确的顺序闭合。

要求:如果合法就输出合法,如果不合法就删除不合法的字符,使其成为合法字符串。

思想:采用双指针策略,判断每个括号的匹配情况,如果匹配成功则输出合法;否则如果有一个括号匹配不成功则输出不合法并且输出修改后的字符串

public static void main(String[] args) {
    String str = "(({[]})]";
    boolean res = Check(str);
    if(res==true){
        System.out.println(str+"字符串合法");
    }else {
        System.out.println(str+"字符串不合法");
    }
}
//采用双指针的策略,如果i指向(,则向后寻找;如果过指向[,则向后寻找],如果指向{则向后寻找};
//若有一次未找到,则该字符串非法,并删除i所对应的字符
public static boolean Check(String str){
    //将str字符串赋给x数组
    char[] x = str.toCharArray();
    //isVisited数组用来表示位置i的字符是否已经匹配
    boolean[] isVisited= new boolean[x.length];
    for(int i=0;i<x.length;i++){
        isVisited[i]=false;
    }
    boolean allFlag= true;
    for(int i=0;i<x.length;i++){
        //表示x[i]处字符是否匹配
        boolean flag = false;
        for(int j=i+1;j<x.length;j++){
            if(x[i]=='('){
                if (x[j]==')'&&isVisited[j]==false){
                    isVisited[j]=true;
                    flag=true;//匹配成功
                    break;
                }
            }
            if(x[i]=='{'){
                if (x[j]=='}'&&isVisited[j]==false){
                    isVisited[j]=true;
                    flag=true;//匹配成功
                    break;
                }
            }
            if(x[i]=='['){
                if (x[j]==']'&&isVisited[j]==false){
                    isVisited[j]=true;
                    flag=true;//匹配成功
                    break;
                }
            }
            else if(x[i]==']'||x[i]=='}'||x[i]==')'){
                if(isVisited[i]==false){
                    flag=false;break;
                }else {
                    flag=true;break;
                }
            }
        }
        //局部不合法则整个字符串都不合法,注意这里要考虑该点是否被匹配,如果未被匹配则需要删除
        if(flag==false&&isVisited[i]==false){
            allFlag=false;
            //用字符d代替要删除的元素
            x[i]='d';
        }
    }
    StringBuilder sb =new StringBuilder();
    for(int i=0;i<x.length;i++){
        if(x[i]!='d'){
            sb.append(x[i]);
        }
    }
    if(allFlag==false){
        System.out.println("修改后的合法字符串为"+sb);
    }
    else {
        System.out.println("合法字符串为"+sb);
    }
    return allFlag;
}

No.3 最大连续子序列和

【题目】:

有n个整数排成一排,求其中最大的连续子序列和。要求使用分治法解决。(这道题我临时没想出来如何使用分治法,所以用了dp。我估计我这题扣了不少分)例如输入如下 6

-10 4 5 7 -11 23

输出如下 4 + 5 + 7 + (-11) + 23=28

思想:采用分治法,首先求出左边的最大连续子序列和,再求出右边最大连续子序列和,最后求出包括中间元素的最大连续子序列和。最后比较三个的大小即可

public static void main(String[] args) {
    int[] array = {-10,4,5,7,-11,23};
    System.out.println(maxAdd(array,0,array.length-1));
}
public static int maxAdd(int[] array,int left,int right){
    //递归终止条件
    if(left==right) return array[left];
    int mid = (left+right)/2;
    int l=left,r=right;
    int leftAdd = maxAdd(array,l,mid);
    int rightAdd = maxAdd(array,mid+1,r);
    int tempAdd = Math.max(leftAdd,rightAdd);
    //计算包括中间元素的左序列的最大序列和
    int ls=0,lefts=0;
    for(int i=mid;i>=l;i--){
        ls+=array[i];
        if(ls>lefts){
            lefts=ls;
        }
    }
    //计算不包括中间元素在内的右序列的最大序列和
    int rs=0,rights=0;
    for(int i=mid+1;i<=r;i++){
        rs+=array[i];
        if(rs>rights){
            rights=rs;
        }
    }
    //左右两边的最大序列和相加得包含中间元素得最大序列和
    int midAdd=rights+lefts;
    //返回最大值
    return (midAdd>tempAdd)?midAdd:tempAdd;
}

No.4

【题目】:

现在有n个任务需要去做,但是由于可能时间冲突不一定全部做。每个任务都有一个开始时间,和完成任务的奖励分。所有任务执行时间都是一天。同一时间只能执行一个任务,且没有相同开始时间且相同的奖励分的任务。要求输出为了得到最大奖励分做哪些任务最好,最大奖励分是多少。

输入如下 4

1 2

2 1

1 1

2 2

输出 1 4 4 表示应该选择第一个任务和第四个任务,最大奖励分是4.

思想:本题宜采用贪心法,即再同一天内选择奖励分最大得任务即可

用数组t来存储任务i得执行时间,用数组v表示执行任务i得到得奖励

public static void main(String[] args) {
    int[] t={1,2,1,2};
    int[] v={2,1,1,2};
    System.out.println("最大的奖励为:"+getMaxValue(t,v,4));
}
public static int getMaxValue(int[] t,int[] v,int n){
    int[] index = new int[n];
    for(int i=0;i<n;i++){
        index[i]=i;
    }
    for(int i=0;i<n;i++){
        for(int j=i+1;j<n;j++){
            if(t[i]>t[j]){
                //实现按照时间递增
                int tempT = t[i];
                t[i]=t[j];
                t[j]=tempT;
                int tempIndex = index[i];
                index[i]=index[j];
                index[j]=tempIndex;
            }
        }
    }
    int maxValue=0;
    //最大的时间
    int maxT = t[n-1];
    //从0开始到最大的天数截止
    for(int i=0;i<=maxT;i++){
        int currentValue = 0;
        for(int j=0;j<n;j++){
            if(t[j]==i&&v[index[j]]>currentValue){
                currentValue=v[index[j]];
            }
        }
        maxValue+=currentValue;
    }
    return maxValue;
}

2018年算法真题

No.1 文件输入输出

/*有8个学生,有如下信息:名字,年龄,成绩。请从键盘输入8位同学的信息,存入文件cs.txt,再从文件中读取出数据,并对8位同学按照成绩由高到低排序,输出成绩第二高的同学的信息。*/
    public static void main(String[] args) throws IOException {
//        writeGrade();
        readGrade();
    }
    public static class Student{
        private String name;
        private int age;
        private int grade;
		//省略get、set、toString、constructor方法
    }
    public static void writeGrade() throws IOException {
        Map<Integer,Student> map = new HashMap<Integer, Student>();
        Scanner scanner = new Scanner(System.in);
        for(int i=1;i<=2;i++){
            Student student = new Student();
            System.out.print("请输入第"+i+"个学生的姓名");
            student.setName(scanner.next());
            System.out.print("请输入第"+i+"个学生的年龄");
            student.setAge(scanner.nextInt());
            System.out.print("请输入第"+i+"个学生的成绩");
            student.setGrade(scanner.nextInt());
            map.put(i,student);
        }
        FileWriter fw = new FileWriter("E://itcast//cs.txt");
        BufferedWriter bw = new BufferedWriter(fw);
        for(int i=1;i<=2;i++){
            bw.write(map.get(i).getName());
            bw.write(" ");
            //注意这里写入整形数据需要将整形转为字符型
            bw.write(String.valueOf(map.get(i).getAge()));
            bw.write(" ");
            bw.write(String.valueOf(map.get(i).getGrade()));
            bw.write("\n");
        }
        bw.close();
        fw.close();
    }
    public static void readGrade() throws IOException {
        FileReader fr = new FileReader("E://itcast//cs.txt");
        BufferedReader br = new BufferedReader(fr);
        String line = br.readLine();
        Student[] students = new Student[8];
        for(int i=0;i<8;i++){
            students[i]=new Student();
        }
        int lineNum=0;
        while (line!=null){
            String[] split = line.split(" ");
            students[lineNum].setName(split[0]);
            students[lineNum].setAge(Integer.parseInt(split[1]));
            students[lineNum].setGrade(Integer.parseInt(split[2]));
            lineNum++;
            line=br.readLine();
        }
        System.out.println("---------------------------");
        //采用冒泡排序对学生成绩进行排序
        for(int i=0;i<8;i++){
            for(int j=i+1;j<8;j++){
                if(students[i].getGrade()<students[j].getGrade()){
                    //交换两学生信息
                    Student temp = new Student(students[j].getName(),students[j].getAge(),students[j].getGrade());
                    students[j].setName(students[i].getName());
                    students[j].setAge(students[i].getAge());
                    students[j].setGrade(students[i].getGrade());
                    students[i].setName(temp.getName());
                    students[i].setAge(temp.getAge());
                    students[i].setGrade(temp.getGrade());
                }
            }
        }
		//输出students[1]的信息
    }

No.2 报数3

【题目】:

有N个同学围成一个圈,并从1到n编号,然后同学们按顺序报数,每次只报1,2,3,报到3的同学退出圆圈,其余的同学继续开始报1,2,3,直到所有同学都退出圆圈。最后输出按出圈顺序的每个同学的编号。

  • 用x数组来判断同学i是否出队,x[i]=0表示未出队;x[i]=1表示已出队

  • 将count与同学k绑定,如果同学k未出队,则此时的count++,再判断count是否可为3整除

    • 如果可,则出队
    • 否则试探下一个同学

    注意:k可能会发生越界,此时需要将k重置

public static void main(String[] args) {
    circleNum(8);
}
public static void circleNum(int N){
    //数组x用来存放编号为i+1的人是否出队列
    int[] x =new int[N];
    for(int i=0;i<N;i++){
        x[i]=0;//0表示编号i+1在队列中
    }
    int count=0;
    int k=0;
    boolean flag=false;
    while (!flag){
        //如果k未被访问则报数+1
        if(x[k]==0){
            count++;
        }
        //数到3的倍数
        if(count%3==0&&x[k]==0){
            x[k]=1;//编号为k+1的出队
            System.out.print((k+1)+" ");
        }
        //下一个同学
        k++;
        //k发生越界时重置k
        if(k>N-1){
            k=0;
        }
        flag=true;
        //循环终止的条件为所有的同学都已出队
        for(int i=0;i<N;i++){
            if(x[i]==0) flag=false;
        }
    }
}

No.3 N皇后

【题目】:

N皇后问题 。在N×N的棋盘上放置N个皇后,且要求任意两个皇后不能在同行同列同对角线上。输入N,输出能摆放成功的情况数。

想法:采用回溯法,x[k]表示在k行x[k]列上放置皇后,这里判断皇后是否可放置的条件为|i-j|==|x[i]-x[j]|表示不可放置,并且不能放在同一列即x[k]!=x[i]

public static int count=0;
public static void main(String[] args) {
    int N =8;
    System.out.println("N皇后放置可能的情况数:"+NQueen(N));
}
public static int NQueen(int N){
    int i,j,k;
    //x[k]表示第i行上放置皇后的位置为x[k]
    int[] x = new int[N];
    for(i=0;i<N;i++){
        x[i]=-1;
    }
    k=0;
    while (k>=0){
        x[k]++;//从位置0开始试探
        while (x[k]<N){
            if(Ok(x,k)) break;
            else x[k]++;
        }
        if(x[k]<N&&k==N-1){
            System.out.print("此时皇后放置的位置为:");
            for(i=0;i<N;i++){
                System.out.print(x[i]+" ");
            }
            System.out.println();
            count++;
        }
        else if(x[k]<N&&k<N-1) k++;//试探下一行
        else{
            x[k--]=-1;//回溯
        }
    }
    return count;
}
//判断此时放置的皇后位置是否发生冲突
public static boolean Ok(int[] x,int k){
    for(int i=0;i<k;i++){
        //皇后的位置不可在同一列、行、斜率为1或-1的斜线上
        if(x[k]==x[i]||Math.abs(i-k)==Math.abs(x[i]-x[k])){
            return false;
        }
    }
    return true;
}

2017年算法真题(专硕)

No.1 链表的删除与插入

【题目】:

​ 在一个带头结点的链表中找出最大值的元素,并且将其删去后把其最大值赋值给头结点。

//定义Node类
public class Node{
    int data;
    Node next;
}
public Node head;
public void findMax(){
    if(head.next==null) return;
    Node p=head.next;
    Node q = head;
    while(q.next!=null){
        q=q.next;
        if(p.data<q.data){
            p=q;//寻找data最大的节点
        }
    }
    //将最大值给到头结点
    int max = p.data;
    head.data=max;
    delete(p);
}
public void delete(Node p){
    if(head.next==null) return;
    Node temp = head;//表示被删除节点的前节点
    Node q=head.next;//表示被删除节点
    while(q!=null){
        if(q.data==p.data){
            temp.next=q.next;
        }
        //
        temp=q;
        q=q.next;
    }
}

No.2 栈的操作

【题目】:

输入一个数n,再输入n个整数,每个整数按先进后出原则(即栈)操作,要求使奇数元素全部在整数元素前打印。(可以进栈若干次后再出栈)

1、实例化

Stack stack = new Stack();

进栈

stack.push(Object);//返回的是入栈的内容
stack.add(Object);//返回的是true或false

出栈

stack.pop();//输出并删除栈顶元素
stack.peek();//输出不删除栈顶元素

判断是否为空

stack.isEmpty();

输出栈([ , , , …])

System.out.println(stack);

查看某元素在栈中的为重,计数从1开始

int index = stack.search(“a”);

No.3 平衡二叉树

写出一个判断平衡二叉树的算法

public boolean is_Balanced_tree(BNode node){
    if(node==null) return true;
    int left = getDepth(node.left);//拿到左子树的高度
    int right = getDepth(node.right);//拿到右子树的高度
    int gap = Math.abs(left-right);//求出左右子树高度差
    if(gap>1)//高度差大于1
        return false;
    else{
        return is_Balanced_tree(node.left)&&isBalanced_tree(node.right);
    }
}
public int getDepth(Node node){
    if(node==null) return 0;
    int left=getDepth(node.left);
    int right=getDepth(node.right);
    return ((left>right)?left:right)+1;
}

2017年算法真题(学硕)

No.1 (贪心,作业处理顺序问题,按照开始时间排序)

【题目】:

img

想法:

​ x[i][2]数组中x[i][0]表示第i个桌子的搬出的教室号,x[i][1]表示第i个桌子搬入的教室号

​ flag数组用来表示当前桌号i是否被搬走

  1. 将x数组按照搬出教室号从小到大进行排序
  2. 开始循环搬的趟数,保证每塘能搬的桌子数量最大,则可保证所用的趟数最小
public static void main(String[] args) {
        Scanner sr =new Scanner(System.in);
        System.out.print("请输入教室数:");
        int n = sr.nextInt();
        System.out.print("清输入桌子数:");
        int m =sr.nextInt();
        int[][] x = new int[m][2];
        System.out.println("清输入每个桌子搬出及搬入的位置;");
        for(int i=0;i<m;i++){
            x[i][0]=sr.nextInt();
            x[i][1]=sr.nextInt();
        }
//        int[][] x ={{3,5},{1,2},{4,6}};
        System.out.println("最少要搬的趟数为:"+moveDesk(x,n,m));
    }
    //x[i][0]表示第i号桌子搬出的位置,x[i][1]表示第i号桌子搬入的位置
    public static int moveDesk(int[][] x,int n,int m){
        //flag数组用来表示当前桌号i是否被搬走
        boolean[] flag = new boolean[m];
        //初始化flag数组
        for(int i=0;i<m;i++){
            flag[i]=false;
        }
        //将x数组按照搬出的位置从小到大排序
        for(int i=0;i<m;i++){
            for(int j=i+1;j<m;j++){
                if(x[i][0]>x[j][0]){
                    int temp0 = x[i][0];
                    x[i][0]=x[j][0];
                    x[j][0]=temp0;
                    int temp1=x[i][1];
                    x[i][1]=x[j][1];
                    x[j][1]=temp1;
                }
            }
        }
        //计算需要的趟数
        int count=0;
        int i=0;//表示已经搬的数
        while(i<m){
            //每趟搬桌子的起始位置
            int start = x[i][0];
            //在本趟中选取最多的桌子数
            for(int j=i;j<m;j++){
                if(flag[j]==false&&start<=x[j][0]){
                    flag[j]=true;
                    i++;
                    //下一个桌子的起始位置
                    start=x[j][1];
                }
            }
            count++;
        }
        return count;
    }

No.2 凑硬币

见2019 No.1

No.3 Nim游戏

【leetcode292】你和你的朋友,两个人一起玩 Nim 游戏:

桌子上有一堆石头。
你们轮流进行自己的回合,你作为先手。

  • 每一回合,轮到的人拿掉 1 - 2 块石头。

  • 拿掉最后一块石头的人就是获胜者。

  • 假设你们每一步都是最优解。

    请编写一个函数,来判断你是否可以在给定石头数量为 n 的情况下赢得游戏。如果可以赢,返回 true;否则,返回 false 。

显而易见的是,如果石头堆中只有一块、两块石头,那么在你的回合,你就可以把全部石子拿走,从而在游戏中取胜。

而如果就像题目描述那样,堆中恰好有三块石头,你就会失败。因为在这种情况下不管你取走多少石头,总会为你的对手留下几块,使得他可以在游戏中打败你。因此,要想获胜,在你的回合中,必须避免石头堆中的石子数为 3 的情况。

同样地,如果有四块、或是五块石头,你可以控制自己拿取的石头数,总是恰好给你的对手留下三块石头,使他输掉这场比赛。但是如果石头堆里有六块石头,你就不可避免地会输掉,因为不管你从一堆石头中挑出一块、还是两块,你的对手都可以选择一块或两块,以确保在再一次轮到你的时候,你会面对三块石头。

显然,它以相同的模式不断重复 n=3,6,9,12…基本可以看出是 3的倍数。

public static void main(String[] args) {
    Scanner sr = new Scanner(System.in);
    int n= sr.nextInt();
    boolean res = result(n);
    if(res){
        System.out.println("你必赢");
    }else System.out.println("你必输");
}
public static boolean result(int n){
    return (n%3==0)?false:true;
}

2016年算法真题

No.1 递归

【题目】:

用递归求f(n)=f(n-1)+f(n-2) n>2;n=1时 f(n)=1;n=2 时 f(n)=1;

public static void main(String[] args) {
//        用递归求f(n)=f(n-1)+f(n-2) n>2;n=1时 f(n)=1;n=2 时 f(n)=1;
        Scanner sr = new Scanner(System.in);
        System.out.print("清输入要计算的n=");
        int n =sr.nextInt();
        System.out.println("得到的结果为:"+f(n));
    }
    public static int f(int n){
        if(n==1||n==2){
            return 1;
        }
        else {
            return f(n-1)+f(n-2);
        }
    }

No.2 I/O

【题目】:

键盘上输入若干个数字,按递减排序,并写入文件中,输入在屏幕上;

public static void main(String[] args) throws IOException {
//        WriteNum();
        ReadNum();
    }
    //写入文件
    public static void WriteNum() throws IOException {
        List<Integer> list = new ArrayList<>();
        Scanner sr =new Scanner(System.in);
        while (sr.hasNext()){
            list.add(sr.nextInt());
            System.out.println(list.toString());
        }
        //将其按照递减排序
        //通过类实现Collections.sort(list)按照递增排序
//        Collections.reverse(list);逆序只是将顺序翻转
        System.out.println(list.toString());
        //重写compare方法 o1-o2为升序,o2-o1为降序
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });
        System.out.println(list.toString());
        FileWriter fw = new FileWriter("E:\\itcast\\hello.txt");
        BufferedWriter bw = new BufferedWriter(fw);
        int len = list.size();
        int i=0;
        while (i<len){
            //这里的整型要转为字符型
            bw.write(String.valueOf(list.get(i)));
            bw.write(" ");
            i++;
        }
        bw.close();
        fw.close();
        sr.close();
    }
    //读出文件
    public static void ReadNum() throws IOException {
        FileReader fileReader = new FileReader("E:\\itcast\\hello.txt");
        BufferedReader bw = new BufferedReader(fileReader);
        String s = bw.readLine();
        String[] x = s.split(" ");
        for(String string:x){
            System.out.print(string+" ");
        }
    }

No.3 线程

【题目】:

在Windows下实现多线程并互斥,主线程休眠2秒,次线程休眠1秒

public static void main(String[] args) {
    Thread t1 = new MyThread("主线程");
    Thread t2 = new MyThread("从线程");
    t1.start();
    t2.start();
}
public static class MyThread extends Thread{
    private String name;
    public MyThread(String name){
        this.name=name;
    }
    public void run(){
        for(int i=1;i<=5;i++){
            synchronized (this) {
                if(this.name.equals("主线程"))
                {
                    try {
                        Thread.sleep(2000);
                        System.out.println("主线程休息2秒");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    try{
                        Thread.sleep(1000);
                        System.out.println("从线程休息1秒");
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }

    }
}

2015年算法真题

No.1 非递归中序搜索

【题目】:

非递归算法求二叉树的只有一个孩子的节点个数(单分支),要求写出节点的结构,详细代码,流程图。类似可用非递归的中序遍历算法求解

//定义节点对象
public static class BNode{
    int data;
    BNode lchild;
    BNode rchild;
}
public BNode root;
public static int inorderTraverse(BNode node){
    Stack stack = new Stack();//初始化栈
    BNode p = node;
    if(p==null) return 0;
    int count=0;//计算单分支节点个数
    while( !p || !stack.isEmpty() ){
          if(!p){                      //根指针进栈,遍历左子树。
              stack.push(p);              //每遇到非空二叉树先向左走。
              p=p.lchild;
          }
         else{                       //根指针退栈,访问根节点,遍历右子树。
             BNode p = stack.pop();               //退栈。
             visit(p);               //访问根节点。
             if((p.lchild==null&&p.rchild!=null)||(p.lchild!=null&&p.rchild==null))//判断是否为单分枝节点
                 count++;
             p=p->rchild;            //再向右子树走
         }
     }
    return count;
}

No.2 报数

【题目】:

n个小孩围成圈,首先输入一个值给max,每个小孩手中有一个密码(通过输入赋值),N个小孩按着顺序报数,当报的数比max大时,此小孩出列,并将手中的密码给max,继续循环,直到最后一个小孩时,此小孩即为获胜者。要求输出小孩的出圈序列和最后胜利的小孩。要求写出详细代码和流程图。

public static void main(String[] args) {
    int n =5;
    //password[i]表示第i个小孩手上的密钥
    int[] password = {5,6,5,8,9};
    int max = 8;
    int num=2;
    System.out.println("最后获胜的小孩序号为:"+getWin(password,n,max,num));
}
public static int getWin(int[] password,int n,int max,int num){
    boolean[] flag = new boolean[n];//flag数组用来表示当前小孩是否出队
    //初始化flag数组
    for(int i=0;i<n;i++){
        flag[i]=false;
    }
    int i=0;//从第0个小孩开始报数
    int boyNum=0;//表示获胜的小孩的编号
    while (true){
        if(flag[i]==false&&num>max){
            boyNum++;
            flag[i]=true;
            max = password[i];
            System.out.print("小孩"+i+"出队");
            if(boyNum==n){
                return i;
            }
        }
        i++;
        num++;
        if(i==n){
            i=0;
        }
        int j;
        for(j=0;j<n;j++){
            if(flag[j]==false) break;
        }
        if(j==n){//如果查到全部的小孩都已经出队则跳出循环
            break;
        }
    }
    return boyNum;

}

No.3 马踏棋盘算法

【题目】:

骑士巡游问题(一般算法书上都有,和马的遍历类似,图的搜索算法)

一个骑士要在8*8的方格内骑马巡游,马只能走日字,骑士要遍历整个方格(注意这里是方格,不是格上的一点),每个方格有且仅有一次,遍历完所有的方格,要求输出遍历方案。要求写出详细代码和流程图

思想:采用回溯法,本道题的重点在于马走的下一个位置有至多8种情况,要利用List来存储Point;

回溯:visited[row*X+column]=false;
chessBoard[row][column]=0;

//列号
public static int X=8;
//行号
public static int Y=8;
//表示该位置是否被访问过
public static boolean visited[];
//表示是否所有的位置都已经被访问
public static boolean finished;
public static void main(String[] args) {
    //马初始位置
    int row=1;
    int column=1;
    int[][] chessBoard = new int[Y][X];
    visited = new boolean[X*Y];
    boolean res = travelChessBoard(chessBoard, row - 1, column - 1, 1);
    if(res){
        System.out.println("马踏棋盘成功");
    }else System.out.println("马踏棋盘失败");
    //输出棋盘的最后情况
    for(int[] rows : chessBoard) {
        for(int step: rows) {
            System.out.print(step + "\t");
        }
        System.out.println();
    }
}
//step=1从第一步开始,chessBoard[i][j]表示该位置在step次访问,row,column表示当前马的位置
public static boolean travelChessBoard(int[][] chessBoard,int row,int column,int step){
    //得到当前马所在的位置能够到达的下一个位置
    ArrayList<Point> ps = getNext(new Point(column,row));
    chessBoard[row][column]=step;
    //表示当前马的位置已被访问
    visited[row*X+column]=true;
    //遍历ps上可访问的节点
    while (!ps.isEmpty()){
        //拿到ps的第一个节点
        Point p2 = ps.remove(0);
        if(!visited[p2.y*X+p2.x]){
            travelChessBoard(chessBoard,p2.y,p2.x,step+1);
        }
    }
    //如果棋盘在上面的访问后未全部遍历完则进行回溯
    if(step<X*Y&&!finished){
        visited[row*X+column]=false;
        chessBoard[row][column]=0;
    }else//完成整个棋盘的访问
        finished = true;
    return finished;
}
//拿到马下次可以走的点的集合,最多有八个位置
public static ArrayList<Point> getNext(Point curPoint){
    //创建一个ArrayList
    ArrayList<Point> ps = new ArrayList<Point>();
    //创建一个Point
    Point p1 = new Point();
    //表示马儿可以走5这个位置
    if((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y -1) >= 0) {
        ps.add(new Point(p1));
    }
    //判断马儿可以走6这个位置
    if((p1.x = curPoint.x - 1) >=0 && (p1.y=curPoint.y-2)>=0) {
        ps.add(new Point(p1));
    }
    //判断马儿可以走7这个位置
    if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y - 2) >= 0) {
        ps.add(new Point(p1));
    }
    //判断马儿可以走0这个位置
    if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y - 1) >= 0) {
        ps.add(new Point(p1));
    }
    //判断马儿可以走1这个位置
    if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y) {
        ps.add(new Point(p1));
    }
    //判断马儿可以走2这个位置
    if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y) {
        ps.add(new Point(p1));
    }
    //判断马儿可以走3这个位置
    if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y) {
        ps.add(new Point(p1));
    }
    //判断马儿可以走4这个位置
    if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y) {
        ps.add(new Point(p1));
    }
    return ps;
}

2013年算法真题

No.3 迷宫问题

【题目】:

一个8*8的棋盘上有一个骑士,骑士每次上下左右走一步,要求能不重复经过一个格子的遍历棋盘。求某种走法。

本题思想与上一题类似,只是需要注意骑士当前节点的下一个节点的计算有所不同

//列号
public static int X = 8;
//行号
public static int Y =8;
//visited数组用来表示该位置是否被访问过
public static boolean visited[];
//finished表示是否棋盘上的所有位置都被访问了
public static boolean finished;
public static void main(String[] args) {
    int row = 1;
    int column=1;
    int[][] chessBoard = new int[Y][X];
    visited = new boolean[X*Y];
    boolean res = travelChessBoard(chessBoard, row - 1, column - 1, 1);
    if(res){
        System.out.println("完成对该棋盘的访问");
    }else {
        System.out.println("未完成对该棋盘的访问");
    }
    for(int[] rows : chessBoard) {
        for(int step: rows) {
            System.out.print(step + "\t");
        }
        System.out.println();
    }

}
//chessBoard数组用来表示当前的位置在第几次访问
//row,column表示骑士当前所在的位置
//step表示当前进行的步数
public static boolean travelChessBoard(int[][] chessBoard,int row,int column,int step){
    chessBoard[row][column]=step;
    visited[row*X+column]=true;//标记该位置已经被访问
    ArrayList<Point> ps = getNext(new Point(column, row));
    while (!ps.isEmpty()){
        //取出ps链表中的第一个节点
        Point p2 = ps.remove(0);
        //判断该点是否被访问过
        if(!visited[p2.y*X+p2.x]){
            travelChessBoard(chessBoard,p2.y,p2.x,step+1);
        }
    }
    //回溯
    if(step<X*Y&&!finished){
        chessBoard[row][column]=0;
        visited[row*X+column]=false;//取消对该位置的访问
    }else finished=true;
    return finished;
}

//寻找当前骑士所在位置可以走的下一个点的位置
public static ArrayList<Point> getNext(Point p){
    ArrayList<Point> ps = new ArrayList<>();
    Point p1 = new Point();
    if((p1.x=p.x-1)>=0){
        p1.y=p.y;
        ps.add(new Point(p1));
    }
    if((p1.x=p.x+1)<X){
        p1.y=p.y;
        ps.add(new Point(p1));
    }
    if((p1.y=p.y-1)>=0){
        p1.x=p.x;
        ps.add(new Point(p1));
    }
    if((p1.y=p.y+1)<Y){
        p1.x=p.x;
        ps.add(new Point(p1));
    }
    return ps;
}

No.4 TSP

【题目】:

某个省的一个城市出发遍历这个省的所有城市,终点要求是起点。求最短路径(就是图的最短路径)##

Mooc

求解畜栏问题(贪心法)

题目内容:

有n头牛(1<=n<=50,000)要挤奶。给定每头牛挤奶的时间区间A,B。牛需要呆在畜栏里才能挤奶。一个畜栏同一时间只能容纳一头牛。问至少需要多少个畜栏,才能完成全部挤奶工作,以及每头牛都放哪个畜栏里?注意:在同一个畜栏的两头牛,它们挤奶时间区间不能在端点重合。

输入格式:

第1行:一个正整数N;
第2…N+1行:第i+1行的两个整数给出第i头奶牛的挤奶时间。

输出格式:

需要畜栏的最小数

输入样例:

5

1 10

2 4

3 6

5 8

4 7

输出样例:

4

使用A[i][0]表示第i头牛的挤奶开始时间,用A[i][1]表示第i头牛的挤奶结束时间。count表示初试的最大畜栏数n
使用两层循环,第一层循环i从第1头牛开始直到最后,第二层循环从第0头牛开始直到第i-1头牛,使用相容条件判断(A[i][0]>A[j][1],如果条件成立则代表有两头牛的挤奶时间段上可以相容,则count--表示畜栏数-1,并且跳出循环;否则count数不变
    for(int i=0;i<n;i++){
        for(int j=0;j<i;j++){
            if(A[i][0]<A[j][1]){//表示挤奶时间段相容
                count--;//畜栏数-1
            	break;
            }
        }
    }

求解区间覆盖问题(贪心法)

题目内容:

设x1,x2,… ,xn是实直线上的n个点。用固定长度的闭区间覆盖这n个点,至少需要多少个这样的固定长度闭区间?设计求解此问题的有效算法。对于给定的实直线上的n个点和闭区间的长度k,编程计算覆盖点集的最少区间数。

输入格式:

输入数据的第一行有2个正整数n和k,表示有n个点,且固定长度闭区间的长度为k。接下来的1行中,有n个整数,表示n个点在实直线上的坐标(可能相同)。

输出格式:

将编程计算出的最少区间数输出。

输入样例:

7 3

1 2 3 4 5 -2 6

输出样例:

3

算法思想:判断如果x.length==1return 1;
    之后将x[n]数组按照从小到大排序,从i=0出开始执行循环直到i==n,在循环中找到第一个j,是的i与j的距离大于k,然后count++;i=j;
if(x.length==1) return 1;
将x数组排序
int i=0;
int count=0;//表示最少的区间数
while(i<n){
    int j=i+1;
    while(x[j]-x[i]<=k) j++;
    i=j;
    count++;
}

拦截导弹问题(动态规划)

题目内容:

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。

输入格式:

第一行,输入雷达捕捉到的敌国导弹的数量k(k<=25),第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。

输出格式:

输出只有一行,包含一个整数,表示最多能拦截多少枚导弹。

输入样例:

8

300 207 155 300 299 170 158 65

输出样例:

6

算法思想:采用动态规划的方法,本题类似于最长递减子序列,用L[i]表示从0至i的最长递减子序列的长度
   	首先初始化L[i]1,然后开始循环
    for(int i=1;i<n;i++){
       	int max =1;
        for(int j=0;j<i;j++){
            if(a[i]<=a[j]&&L[i]<L[j]+1){
                max = L[j]+1;
                L[i]=max;
            }
        }
    }
	然后取出L数组中最大的值即可

新水果取名(动态规划)

题目内容:

两种水果杂交出一种新水果,现在给新水果取名,要求这个名字中包含以前两种水果的字母,且名字尽量短,即:以前的水果名字arr1、arr2是新水果名arr的子序列,使用动态规划的思想设计算法得到新水果名arr。

输入格式:

以空格分开两个水果的名字

输出格式:

新水果的名字

输入样例:

pear peach

输出样例:

pearch

输入样例:

peach pear

输出样例:

peachr

public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		//读入两个字符串
		String string = scanner.nextLine();
		String[] split = string.split(" ");
		//将第一个字符串转为数组x
		char[] str1 = new char[split[0].length()];
		str1 = split[0].toCharArray();//拿到第一个水果数组
		char[] str2 = new char[split[1].length()];
		str2 = split[1].toCharArray();//拿到第二个水果数组
		int[][] dp = new int[str1.length+1][str2.length+1];//定义数组dp表示将两字符串连在一起时去掉重复字符串的长度
		int[][] s = new int[str1.length+1][str2.length+1];//s状态数组表示两字符串的拼接情况
		//初始化dp数组
		for(int i=0;i<str1.length;i++) {
			dp[i][0] = i;
		}
		for(int i=1;i<str2.length;i++) {
			dp[0][i] = i;
		}
		//初始化s数组
		for(int i=1;i<str1.length;i++) {
			s[i][0] = 2;//读取该元素后向上读取
		}
		//读取该元素后向左读取
		for(int i=1;i<str2.length;i++) {
			s[0][i]=3;
		}
		for(int i=1;i<=str1.length;i++) {
			for(int j=1;j<=str2.length;j++) {
				if(str1[i-1]==str2[j-1]) {
					dp[i][j] = dp[i-1][j-1]+1;//这里需要+1因为表示的是两字符串拼接后的最大长度
					s[i][j] = 1;//表示当前两个字符相等
				}
				else {
					if(dp[i][j-1]>dp[i-1][j]) {
						dp[i][j] = dp[i][j-1] + 1;
						s[i][j] = 2;
					}else {
						dp[i][j] = dp[i-1][j] + 1;
						s[i][j] = 3;
					}
				}
			}
		}
		char[] array = new char[dp[str1.length][str2.length]];
		int num = dp[str1.length][str2.length]-1;
		for(int i = str1.length,j = str2.length;i>=0&&j>=0;) {
			if(s[i][j]==1) {
				array[num] = str1[i-1];
				i--;
				j--;
				num--;
			}else if(s[i][j]==2) {
				array[num] = str1[i-1];
				i--;
				num--;
			}else if(s[i][j]==3) {
				array[num] = str2[j-1];
				j--;
				num--;
			}else {
				break;
			}
		}
		System.out.println(Arrays.toString(array));
	}

机器人路径规划(动态该规划)

题目内容:

一个机器人只能向下和向右移动,每次只能移动一步,设计一个算法求机器人从(1,1)到(m,n)有多少条路径。

输入格式:

以空格分开m,n

输出格式:

路径条数

输入样例:

4 5

输出样例:

35

import java.util.Scanner;

public class robot {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int m = scanner.nextInt();
		int n = scanner.nextInt();
		int[][] path = new int[m][n];
		System.out.println(getPathCounts(path, m, n));
		
	}
	public static int getPathCounts(int[][] path,int m,int n) {
		//初始化第一行
		for(int i=0;i<n;i++) {
			path[0][i]=1;
		}
		//初始化第一列
		for(int i=0;i<m;i++) {
			path[i][0]=1;
		}
		for(int i=1;i<m;i++) {
			for(int j=1;j<n;j++) {
				path[i][j] = path[i-1][j]+path[i][j-1];
			}
		}
		return path[m-1][n-1];
	}
}

求解最小机器重量设计问题(回溯法)

题目内容:

设某一机器由n个部件组成,部件编号为1n,每一种部件都可以从m个不同的供应商处购得,供应商编号为1m。设wij是从供应商j处购得的部件i的重量,cij是相应的价格。对于给定的机器部件重量和机器部件价格,计算总价格不超过d的最小重量机器设计。(注意:输出结果中第一行最后没有空格。比如下面的输出样例中1 3 1后面没有空格。)

输入格式:

第1行输入3个正整数n,m和d。接下来n行输入wij(每行m个整数),最后n行输入cij(每行m个整数),这里1≤n、m≤100。

输出格式:

输出的第1行包括n个整数,表示每个对应的供应商编号,第2行为对应的最小重量。

输入样例:

3 3 7

1 2 3

3 2 1

2 3 2

1 2 3

5 4 2

2 1 2

输出样例:

1 3 1

4

import java.util.Scanner;

public class Main{
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		//n为机器部件数
		int n = scanner.nextInt();
		//m为供应商数
		int m =scanner.nextInt();
		//d为最大价值
		int d = scanner.nextInt();
		int[][] w = new int[n][m];
		//拿到w数组
		for(int i=0;i<n;i++) {
			for(int j=0;j<m;j++) {
				w[i][j] = scanner.nextInt();
			}
		}
		int[][] c = new int[n][m];
		//拿到c数组
		for(int i=0;i<n;i++) {
			for(int j=0;j<m;j++) {
				c[i][j] = scanner.nextInt();
			}
		}
		getMinWeight(n, m, c, w, d);
		
	}
	public static void getMinWeight(int n,int m,int[][] c,int[][] w,int d) {
		//x[k]表示第k个部件的供应商号
		int[] x = new int[n];
		//初始化x[k]数组
		for(int i=0;i<n;i++) {
			x[i]=-1;
		}
		int k =0;
		int[] weight = new int[n];
		int[] value = new int[n];
		int[] temp = new int[n];//用来记录最小重量时的供应商编号
		//初始化最小重量为MAX_VALUE
		int BestWeight = Integer.MAX_VALUE;
		while(k>=0) {
			//试探k部件的下一个供应商
			x[k]++;
			while(x[k]<m) {
				if(k==0) {
					if(c[k][x[k]]<d) {
						value[k]=c[k][x[k]];
						weight[k]=w[k][x[k]];
					}
				}else {
					value[k] = value[k-1]+c[k][x[k]];
					weight[k] = weight[k-1]+w[k][x[k]];
				}
				if(value[k]<=d) break;//满足条件
				else {
					x[k]++;
				}
			}
			if(x[k]<m&&k<n-1) {
				k++;//试探下一个部件
			}else {
				if(x[k]<m&&k==n-1&&value[k]<=d) {
					if(weight[k]<BestWeight) {
						for(int i=0;i<n;i++) {
							temp[i]=x[i];
						}
						BestWeight = weight[k];
					}
				}
				//开始回溯
				x[k--]=-1;
			}
			
		}
		for(int i=0;i<n;i++) {
			if(i==n-1) {
				System.out.print((temp[i]+1));
			}else {
				System.out.print((temp[i]+1)+" ");
			}
			
		}
		System.out.println();
		System.out.println(BestWeight);
	}
}

求解部分和问题(回溯法)

题目内容:

给出N个正整数组成的数组A,求能否从中选出若干个,使他们的和为K。如果可以,输出:“YES”,否则输出"NO"。

输入格式:

第1行:2个数N、K, N为数组的长度, K为需要判断的和(2 ≤N ≤ 20,1 ≤ K ≤ 10^9)

第2 到第 N + 1行:每行1个数,对应数组的元素A[i] (1 ≤ A[i]≤ 10^6)

输出格式:

如果可以,输出:“YES”,否则输出"NO"。

样例输入

4 13

1

2

4

7

样例输出

YES

输入样例:

5 9

1

2

3

4

5

输出样例:

YES

public static void main(String[] args) {
		//给出N个正整数组成的数组A,求能否从中选出若干个,使他们的和为K。如果可以,输出:"YES",否则输出"NO"。
		Scanner scanner = new Scanner(System.in);
		//数组A包含N个整数
		int N = scanner.nextInt();
		//求得的和为K
		int K = scanner.nextInt();
		int[] A = new int[N];
		for(int i=0;i<N;i++) {
			A[i] = scanner.nextInt();
		}
		System.out.println(Arrays.toString(A));
		System.out.println(getCheck(A, N, K));
	}
	public static boolean getCheck(int[] A,int N,int K) {
		//数组x[k]表示第k个选择的数为A[x[k]]
		int[] x= new int[N];
		for(int i=0;i<N;i++) {
			x[i]=-1;
		}
		int[] sum = new int[N];
		//flag数组表示当前A的下标为x[k]的数是否被使用
		boolean[] flag = new boolean[N];
		for(int i=0;i<N;i++) {
			flag[i]=false;
		}
		int k =0;
		while(k>=0) {
			x[k]++;
			while(x[k]<N) {
				if (!flag[x[k]]) {
					if(k==0) {
						sum[k] = A[x[k]];
					}else {
						sum[k] = sum[k-1]+A[x[k]];
					}
					if(sum[k]<=K) {
						//将此时的x[k]下标设置为已被访问
						flag[x[k]]=true;
						break;
					}else {
						
						x[k]++;
					}
				}
				else {
					x[k]++;//继续试探A中的下一个数
				}
			}
			//表示找到符合题目条件的结果
			if(x[k]<N&&sum[k]==K) {
				return true;
			}
			if(x[k]<N&&sum[k]<K) {
				k++;
			}
			else {
				x[k]=-1;//进行回溯
				flag[x[k]]=false;
				k--;
			}
		}
		
		return false;
	}

机试真题

/**
	 * 小明玩一个游戏,小明手上有一张牌,桌子上有六张牌,游戏规则是,求用最多的桌上卡牌与小明手中的卡牌进行加减乘除四则运算 最后使
	   果达到13.如小明卡牌是5,桌上卡牌是1 3 9 4 2 2,此题答案即5+1/3+9+4-2
       即最多可容纳5张牌(此题不考乘除优先级,先到优先,桌上卡牌可重)。
	 */
	public static void main(String[] args) throws Exception {
		int[] a = {1,3,9,4,2,2};
		int[] op = new int[10];
		dfs(a, 0, 6, 5, op);
	}
	public static void dfs(int[] a,int deep,int n,int res,int[] op) {
		if(deep==n) {
			if(res==13) {
				System.out.print("5");
				for(int i=0;i<n;i++) {
					if(op[i]==1) {
						System.out.print("+");
					}
					else if (op[i]==2) {
						System.out.print("-");
					}
					else if(op[i]==3) {
						System.out.print("*");
					}
					else if (op[i]==4) {
						System.out.print("/");
					}
					else if(op[i]==5) {
						continue;
					}
					System.out.print(a[i]);
				}
				System.out.print("=13");
				System.out.println();
			}
			return;
		}else {
			op[deep]=1;
			dfs(a, deep+1, n, res+a[deep], op);
			op[deep]=2;
			dfs(a, deep+1, n, res-a[deep], op);
			op[deep]=3;
			dfs(a, deep+1, n, res*a[deep], op);
			op[deep]=4;
			dfs(a, deep+1, n, res/a[deep], op);
			op[deep]=5;
			dfs(a, deep+1, n, res, op);
		}
	}
package shangji;

import java.awt.Point;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.sql.PseudoColumnUsage;
import java.util.Arrays;

public class t2018 {
//	小硕在点1处,导师在点n处,小硕可以通过中间任意n-2个点到达导师处,且小硕每次到达的下一个点比当前点序号值大。
//	但这其间有要求不能通过哪些路径,例如,要求不能通过1-2-3路径,但此时1-2,1-3均可。
//	每个点都有相应的坐标x,y,最终求小硕在满足要求的条件下,到达导师处的最短路径长度。
//	输入输出均采用文件,格式如下:
//	输入input.txt
//	第一行,为导师点序号n(即总共点的个数n),以及需要满足的要求的个数m。
//	第二行开始,为n个点的坐标x,y值,一行一个点,先x后y。
//	接下来是要求,每个要求有两行数据,第一行是该要求关联到的点的个数k,第二行是具体要求路径。
//	示例:
//	3 1
//	1 1
//	2 1
//	3 1
//	2
//	1 2
//	(该示例按上面的解释就是:总共有3个点,小硕在点1,导师在点3;三个点的坐标分别为(1,1),(2,1),(3,1)。
//	小硕到达导师处有一个要求,该要求关联2个点,点1点2,即不能通过路径1-2)
//	输出output.txt
//	输出只有一行,若能到达,输出最短路径,并保留两位小数,如上述示例中的输出结果为2.00;
//	若不能到达,则输出一句话(一句英文,意思就是不能到达,我记不清具体句子了,好像是can not reach啥的)。
	public static void main(String[] args) throws Exception{
		FileReader fReader = new FileReader("E://itcast//input.txt");
		BufferedReader bReader = new BufferedReader(fReader);
		//读取第一行的点,包括小硕在的点和导师在的点
		String[] split = bReader.readLine().split(" ");
		//拿到小硕所在的点的位置
		int start = Integer.parseInt(split[1]);
		//拿到导师所在的点的位置
		int n = Integer.parseInt(split[0]);
		//将拿到的点存放在对象数组中
		Point[] points = new Point[n];
		//初始化对象数组
		for(int i=0;i<n;i++) {
			points[i] = new Point();
		}
		//读取n个输入的点
		for(int i=0;i<n;i++) {
			String[] split2 = bReader.readLine().split(" ");
			//拿到对应点的x、y坐标
			points[i].x = Integer.parseInt(split2[0]);
			points[i].y = Integer.parseInt(split2[1]);
		}
		//从文件中拿到要求关联的点数
		int forbidN = Integer.parseInt(bReader.readLine());
		//forbidPoints用来表示不能通过的点的序号
		int[] forbidPoints = new int[forbidN];
		//从文件中拿到不能经过的点的序号
		String[] split3 = bReader.readLine().split(" ");
		//将拿到的序号赋值个forbidPoints数组
		for(int i=0;i<forbidN;i++) {
			//注意x[k]是从0号顶点开始的
			forbidPoints[i] = Integer.parseInt(split3[i])-1;
		}
		bReader.close();
		fReader.close();
		//算法思想:
		//下面采用回溯法遍历所有的剩余n-1个节点,用数组x[k]表示第k个选取的节点为x[k]
		//注意x[k]需要满足两个条件:
		//	1.其值大于x[k]之前的元素的值,因为由题目要求可知,所选的点序号必须是逐渐增大的
		//	2.所选的x[k-1],x[k]两组点不在关联到的点forbidPoints数组中;如果在关联的点中,则进行回溯操作x[k--]=0,进行剪枝操作
		//同时在遍历过程中设置BestLength用来标记最小的路径长度初始化为10000(假定为最大值),用ComLength[k]用来表示走k步的路径长度,
		//在每次将x[k]加入路径队列后需要使ComLength[k]+=distance(points[x[k-1]],points[x[k]]),如果ComLength[k]>BestLength则开始回溯:x[k--]=0,进行剪枝操作
		//如果ComLength<BestLength则继续试探k+1步应该走的节点的序号
		//如果通过上述操作BestLength得值任然为1000,则返回can not reach;否则返回x[k]数组,并且将返回得内容写入到文件output.txt中
		//用来记录第k步所走得节点的序号
		int[] x = new int[n];
		//ComLength[k]用来表示走k步的路径长度
		double[] comLength = new double[n];
		for(int i=0;i<n;i++) {
			x[i]=0;//初始化第i步节点序号
			comLength[i]=0;//初始化路径长度为0
		}
		int k = 1;//k从1开始
		double BestLength = 10000;//设置最大的路径长度
		int[] tempX = new int[n];//用来存储最小路径长度所在的顶点的序号
		for(int i=0;i<n;i++) {
			tempX[i]=-1;
		}
		while(k>=1) {
			x[k]++;//选取下一个顶点
			while(x[k]<n) {
				if(Ok(x,k,forbidPoints)) {
					comLength[k]=comLength[k-1]+Distance(points[x[k-1]],points[x[k]]);
					break;
				}else {
					x[k]++;//试探下一个顶点序号
				}
			}
			if(x[k]<n-1) {
				k++;
			}else {
				if(x[k]==n-1&&comLength[k]<BestLength) {
					BestLength = comLength[k];
					for(int i=0;i<=k;i++) {
						tempX[i]=x[i];//将路径上的节点给到tempX
					}
				}
				x[k--]=0;//进行回溯
			}
		}
//		StringBuilder stringBuilder =new StringBuilder();//用来保存路径序列
		FileWriter fileWriter =new FileWriter("E://itcast//output.txt");
		BufferedWriter bWriter = new BufferedWriter(fileWriter);
		System.out.println("----------");
		if(BestLength!=10000) {
//			for(int i=0;i<n;i++) {
//				//tempX[i]==-1即表示当前顶点不在路径中
//				if(tempX[i]!=-1) {
//					stringBuilder.append(tempX[i]+1);
//					stringBuilder.append(" ");
//				}
//			}
//			System.out.println(stringBuilder);
			//将最短路径写入文件output.txt并保留小数点后两位
			String string = String.format("%.2f", BestLength);
			System.out.println(string);
			bWriter.write(string);
		}
		else {
			System.out.println("can not reach");
			bWriter.write("can not reach!!");
		}
//		System.out.printf(String.format("%.2f", BestLength));
		bWriter.close();
		fileWriter.close();
		
	}
	//判断x[k]是否满足以下两个条件:
//	1.其值大于x[k]之前的元素的值,因为由题目要求可知,所选的点序号必须是逐渐增大的
//	2.所选的x[k-1],x[k]两组点不在关联到的点forbidPoints数组中;如果在关联的点中,则进行回溯操作x[k--]=0,进行剪枝操作
	public static boolean Ok(int[] x,int k,int[] forbidPoints) {
		//首先判断x[k]的值是否是递增的即顶点的序号是增加的
		for(int i=0;i<k;i++) {
			//如果有一个元素非递减,那么返回false
			if(x[i]>=x[k]) return false;
		}
		//下面判断所选的点是否在关联的点中
		for(int j=0;j<k;j++) {
			if(forbidPoints[j]==x[k-1]&&forbidPoints[j+1]==x[k]) {
				return false;
			}
		}
		return true;
	}
	
	//计算两点p1和p2间的距离
	public static double Distance(Point p1,Point p2) {
		return Math.sqrt(Math.pow(p1.x-p2.x, 2)+Math.pow(p1.y-p2.y, 2));
	}
}

  • 6
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值