2013年第四届蓝桥杯Java程序设计本科B组决赛 九宫重排(编程大题)

2013年第四届蓝桥杯Java程序设计本科B组决赛个人题解汇总:

https://blog.csdn.net/daixinliangwyx/article/details/89946814

 

第四题

标题:九宫重排

交题测试链接:http://lx.lanqiao.cn/problem.page?gpid=T42

如下面第一个图的九宫格中,放着  1~8  的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。

我们把第一个图的局面记为:12345678. 
把第二个图的局面记为:123.46758 
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。 
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。

例如:
输入数据为:
12345678.
123.46758
则,程序应该输出:
3

再如:
输入:
13524678.
46758123.
则,程序输出:
22


资源约定:
峰值内存消耗(含虚拟机) < 64M
CPU消耗  < 2000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。


解法:BFS+康拓展开判重。

求最少步数,很明显应该用bfs来减少耗时,开一个结构体存当前整个九宫格的形态(用数组记录)、当前'.'的位置、当前所用步数,从起点'.位置'开始,暴力的将周围的数字都与'.'进行交换,将每次交换后得到的形状存起来与终态形状比对从而判断是否达到终态。在一圈圈的往外交换的过程中,到达终态时实际上已经是最小步数了。这里注意判重,即通过一定次数的交换可能会得到之前得到过的形状,这个时候就不需要将出现过的形状加进队列里了。判重的话,可以通过康拓展开来做,康拓展开可以得到某种排列方式在全排列下属于第几个,整个九宫格的形状实际上就是可以看作1-9全排列的一种排列方式(需要预处理将'.'替换成数字9),九宫最多有9!种排列组合,即362880种排列组合方式,可以使用数组vis[1000000]来判断是否出现重复的排列组合方式。

代码:

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.*;

public class Main {
    public static InputReader in = new InputReader(new BufferedInputStream(System.in));
    public static PrintWriter out = new PrintWriter(System.out);
    public static int[] res = new int[9];
    public static int[] map = new int[9];
    public static int[] vis = new int[1000000];
    public static String s;
    public static int start;
    public static int[] dir = {1, -1, 3, -3};
    public static int[] jc = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};///1到8的阶乘

    public static void main(String[] args) {
        s = in.nextLine();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '.') {
                start = i;
                map[i] = 9;
            } else {
                map[i] = s.charAt(i) - '0';
            }
        }
        s = in.nextLine();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '.') res[i] = 9;
            else
                res[i] = s.charAt(i) - '0';
        }
        out.println(bfs());
        out.close();
    }

    static class Point {
        int[] a;
        int location, step;
    }

    static int bfs() {
        ArrayDeque<Point> q = new ArrayDeque<>();
        Point now, next;
        now = new Point();
        now.a = new int[9];
        now.a = map;
        now.location = start;
        now.step = 0;
        q.offerLast(now);
        vis[por(now.a)] = 1;
        while (q.isEmpty() == false) {
            now = q.pollFirst();
            if (checkEnd(now.a) == 1) return now.step;
            for (int i = 0; i < 4; i++) {
                if ((now.location == 3 || now.location == 6) && i == 1) continue;//最左边一列不能减1
                if ((now.location == 2 || now.location == 5) && i == 0) continue;//最右边一列不能加1
                next = new Point();
                next.location = now.location + dir[i];
                next.step = now.step + 1;
                next.a = new int[9];
                for (int j = 0; j < now.a.length; j++)
                    next.a[j] = now.a[j];
                if (next.location < 0 || next.location > 8) continue;
                next.a[now.location] = next.a[next.location];
                next.a[next.location] = 9;
                int kt = por(next.a);
                if (vis[kt] == 0) {
                    vis[kt] = 1;
                    q.offerLast(next);
                }
            }
        }
        return -1;
    }

    static int por(int[] a) {//康拓展开,求一种排列方式在全排列下属于第几个
        int size = 0;
        for (int i = 0; i < 9; i++) {
            int len = 0;
            for (int j = i + 1; j < 9; j++) {
                if (a[i] > a[j]) len++;
            }
            size += len * jc[8 - i];
        }
        return size;
    }

    static int checkEnd(int[] a) {
        for (int i = 0; i < 9; i++)
            if (a[i] != res[i]) return 0;
        return 1;
    }

    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public String nextLine() {
            String str = null;
            try {
                str = reader.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public Double nextDouble() {
            return Double.parseDouble(next());
        }

        public BigInteger nextBigInteger() {
            return new BigInteger(next());
        }

    }
}

评测结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值