POJ 1021 2D-Nim(JAVA实现)

2D-Nim
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 4409 Accepted: 2021
Description

The 2D-Nim board game is played on a grid, with pieces on the grid points. On each move, a player may remove any positive number of contiguous pieces in any row or column. The player who removes the last piece wins. For example, consider the left grid in the following figure.

The player on move may remove (A), (B), (A, B), (A, B, C), or (B,F), etc., but may not remove (A, C), (D, E), (H, I) or (B, G).
For purposes of writing 2D-Nim-playing software, a certain programmer wants to be able to tell whether or not a certain position has ever been analyzed previously. Because of the rules of 2D-Nim, it should be clear that the two boards above are essentially equivalent. That is, if there is a winning strategy for the left board, the same one must apply to the right board. The fact that the contiguous groups of pieces appear in different places and orientations is clearly irrelevant. All that matters is that the same clusters of pieces (a cluster being a set of contiguous pieces that can be reached from each other by a sequence of one-square vertical or horizontal moves) appear in each. For example, the cluster of pieces (A, B, C, F, G) appears on both boards, but it has been reflected (swapping left and right), rotated, and moved. Your task is to determine whether two given board states are equivalent in this sense or not.
Input

The first line of the input file contains a single integer t (1 ≤ t ≤ 10), the number of test cases, followed by the input data for each test case. The first line of each test case consists of three integers W, H, and n (1 ≤ W, H ≤ 100). W is the width, and H is the height of the grid in terms of the number of grid points. n is the number of pieces on each board. The second line of each test case contains a sequence of n pairs of integers xi , yi, giving the coordinates of the pieces on the first board (0 ≤ xi < W and 0 ≤ yi < H). The third line of the test case describes the coordinates of the pieces on the second board in the same format.
Output

Your program should produce a single line for each test case containing a word YES or NO indicating whether the two boards are equivalent or not.
Sample Input

2
8 5 11
0 0 1 0 2 0 5 0 7 0 1 1 2 1 5 1 3 3 5 2 4 4
0 4 0 3 0 2 1 1 1 4 1 3 3 3 5 2 6 2 7 2 7 4
8 5 11
0 0 1 0 2 0 5 0 7 0 1 1 2 1 5 1 3 3 6 1 4 4
0 4 0 3 0 2 1 1 1 4 1 3 3 3 5 2 6 2 7 2 7 4
Sample Output

YES
NO

====================================================
题目解析:
即比较左图中的连通Group,是否可以通过平移、翻转、镜像等方式,与右图能相等。

解题思路:
先计算左图中的所有连通Group,将每个连通Group的所有两点之间距离相加作为Group的特征量Hash,将左右两图所有Group的特征量Hash进行排序对比是否一致。
备注:在POJ上AC通过了,因为上述算法是必要条件,测试数据不是很严格所以通过了。比较好的改进应该是记录每个点到所在Group的其他点的距离和(甚至就是到每个点的距离的列表)作为特征量Hash,然后对比,这样对比的算法就严格很多,但也属于必要条件,充分条件未知。

JAVA代码:

import java.io.*;
import java.util.*;

class node{                     //节点的类
    public int x;               //节点x轴坐标
    public int y;               //节点y轴坐标
    public boolean already;     //这个节点是否已经计算过连通图
    public boolean group;       //这个节点是否本轮计算连通图的一部分

    node(int input_x,int input_y){
        x=input_x;
        y=input_y;
        already=false;
        group=false;
    }
}

public class Main {
    static Scanner in = new Scanner(System.in);

    public static void main(String[] args) {
        int caseNum = in.nextInt(); //输入用例的个数
        boolean isSame;             //对比结果
        int maxX = 0;               //当前用例的x轴最大值
        int maxY = 0;               //当前用例的y轴最大值
        int nodeNum = 0;            //当前用例的最大节点数
        double[] hashLeft;          //左图所有Group的两点之间距离和
        double[] hashRight;         //右图所有Group的两点之间距离和

        for (; caseNum > 0; caseNum--) {
            isSame = true;
            maxX = in.nextInt();
            maxY = in.nextInt();
            nodeNum = in.nextInt();
            hashLeft = new double[nodeNum];
            hashRight = new double[nodeNum];

            for (int i = 0; i < nodeNum; i++) {     //初始化全部为0
                hashLeft[i] = 0;
                hashRight[i] = 0;
            }

            hashLeft = calcHash(maxX, maxY, nodeNum);   //计算左图所有Group的两点之间距离和
            hashRight = calcHash(maxX, maxY, nodeNum);  //计算右图所有Group的两点之间距离和
            System.out.println(compareSame(hashLeft, hashRight) ? "YES" : "NO");    //排序后比较左右图所有Group的两点之间距离和是否一致
        }
    }


    public static double[] calcHash(int maxX, int maxY, int nodeNum) {      //计算所有Group的两点之间距离和
        node[] nodeArray = new node[nodeNum];                               //将图中所有的节点放入节点数组以方便遍历
        Map<Integer, node> mapNode = new HashMap<Integer, node>();          //将图中所有的节点的XY轴坐标的值作为Key映射节点
        double[] groupHash = new double[nodeNum];                           //将图中所有的Group的两点之间距离和存入数组以进行对比
        int goupHashIndex = 0;                                              //Group的索引从0开始递增
        int xi, yi;

        for (int i = 0; i < nodeNum; i++) {                 //读取图中的每个节点,放入节点数组,并建立坐标与节点的映射
            xi = in.nextInt();
            yi = in.nextInt();
            nodeArray[i] = new node(xi, yi);
            mapNode.put(xi * 1000 + yi, nodeArray[i]);
        }

        for (int j = 0; j < nodeNum; j++) {                 //遍历所有节点开始计算所有连通图以及对应的两点之间距离和
            if (nodeArray[j].already == false) {
                calcGroup(nodeArray, nodeArray[j].x, nodeArray[j].y, mapNode, maxX, maxY);      //从未计算过Group的点开始用种子填充算法计算其Group
                groupHash[goupHashIndex++] =calcGroupHash(nodeArray);                           //对本轮计算出的Group,计算两点之间距离和
            }
        }
        return groupHash;
    }


    public static void calcGroup(node[] nodeArray, int x, int y, Map<Integer, node> mapNode, int maxX, int maxY) {  //从未计算过Group的点开始用种子填充算法计算其Group
        mapNode.get(x * 1000 + y).already = true;
        mapNode.get(x * 1000 + y).group = true;

        if (y + 1 <= maxY) {    //计算上面的点是否连通
            if (mapNode.containsKey(x * 1000 + y + 1) && (mapNode.get(x * 1000 + y + 1).already == false)) {
                calcGroup(nodeArray, x, y + 1, mapNode, maxX, maxY);
            }
        }

        if (y - 1 >= 0) {       //计算下面的点是否连通
            if (mapNode.containsKey(x * 1000 + y - 1) && (mapNode.get(x * 1000 + y - 1).already == false)) {
                calcGroup(nodeArray, x, y - 1, mapNode, maxX, maxY);
            }
        }

        if (x - 1 >= 0) {       //计算左面的点是否连通
            if (mapNode.containsKey((x - 1) * 1000 + y) && mapNode.get((x - 1) * 1000 + y).already == false) {
                calcGroup(nodeArray, x - 1, y, mapNode, maxX, maxY);
            }
        }

        if (x + 1 <= maxX) {     //计算右面的点是否连通
            if (mapNode.containsKey((x + 1) * 1000 + y) && mapNode.get((x + 1) * 1000 + y).already == false) {
                calcGroup(nodeArray, x + 1, y, mapNode, maxX, maxY);
            }
        }
    }

    public static double calcGroupHash(node[] nodeArray){           //对本轮计算出的Group,计算所有两点之间距离和
        double hashSum = 0;
        for (int i = 0; i < nodeArray.length-1; i++) {
            if (nodeArray[i].group == true) {
                for (int j = i + 1; j < nodeArray.length; j++) {
                    if (nodeArray[j].group == true) {
                        hashSum = hashSum + Math.pow(nodeArray[i].x - nodeArray[j].x, 2) + Math.pow(nodeArray[i].y - nodeArray[j].y, 2);
                    }
                }
            }
        }

        for (int j = 0; j < nodeArray.length; j++) {                //清除本轮Group计算出的连通节点信息
            nodeArray[j].group = false;
        }
        return hashSum;
    }

    public static boolean compareSame(double[] hashLeft, double[] hashRight) {      //排序后比较左右图所有Group的两点之间距离和是否一致
        Arrays.sort(hashLeft);
        Arrays.sort(hashRight);
        return Arrays.equals(hashLeft, hashRight);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值