【华为OD题库-006】五子棋迷-Java

题目

张兵和王武是五子棋一迷,工作之余经常切磋棋艺。这不,这会儿又下起来了。走了一会儿,轮张兵了,对着一条线思考起来了,这条线上的棋子分布如下
用数组表示: -1 0 1 1 1 0 1 0 1 1
棋子分布说明:
1.-1代表白子,0代表空位,1代表黑子
⒉数组长度L,满足1<L<40,且L为奇数
你得帮他写一个程序,算出最有利的出子位置。最有利定义:
1.找到一个空位(0),用棋子(1/-1)填充该位置,可以使得当前子的最大连续长度变大
2.如果存在多个位置,返回最靠近中间的较小的那个坐标;
3.如果不存在可行位置,直接返回-1
4.连续长度不能超过5个(五字棋约束)
输入描述:
第一行:当前出子颜色
第二行:当前的棋局状态
输出描述
1个整数,表示出子位置的数组下标
示例1
输入:
1
-1 0 1 1 1 0 1 0 1 -1 1
输出:
5
说明:
当前为黑子(1),放置在下标为5的位置,黑子的最大连续长度,可以由3到5
示例2
输入:
-1
-1 0 1 1 1 0 1 0 1 -1 1
输出:
1
说明:
当前为白子,唯一可以放置的位置下标为1,白子的最大长度,由1变为2
示例3
输入:
1
0 0 0 0 1 0 0 0 0 1 0
输出:
5
说明:
可行的位置很多,5最接近中间的位置坐标

思路

同leetcode:424. 替换后的最长重复字符

根据题目描述,以示例一为例:
1
-1 0 1 1 1 0 1 0 1 -1 1
可以考虑以下步骤(假定输入的数组为grids):

  1. 计算初始状态下,grids中连续为1的个数(==> tag1),3个
  2. 遍历棋盘grids,找到还未出子的位置(值为0),比如此处的grids[1]=0,假定在此处落子(grids[1]=1),那么从位置1开始计算的连续长度个数为4。(grids1~girds4。==> tag2),最大连续长度从3变成了4,记录此时的最大值cur_max_len=4,以及出子位置bestIdx=1
  3. 找到下一个未出子位置,即grids[5]=0,假定在此处落子(grids[5]=1),那么从5计算的连续长度为5(grids2~girds6),大于当前的cur_max_len,所以更新最大值及落子位置,cur_max_len=5,bestIdx=5;
  4. 继续找下一个未出子位置,假定grids[7]=1,那么,这时候当前连续长度为3,小于最大值cur_max_len,所以舍弃。
  5. 继续遍历,找不到新的落子位置了,结束。返回记录的bestIdx即可。

此外,根据题目2,3,4的约束条件,在步骤3中,当我们得到一个新的值,需用同时满足下面2个条件,才能更新最大值和最佳落子位置:

  1. 得到新的连续长度比当前最大值还要大;或者新的连续长度和当前的最大值相等,但是新的位置更靠近中心
  2. 新的连续长度不大于5

上面的步骤可以拆解出两个问题(tag1和tag2):

问题1. 对于给定数组,怎么计算初始状态下某个值的最大连续长度?
1.1 直观方法:先找到第一个等于给定值的位置,再遍历此位置后面还有几个元素等于该值,得到一个连续长度;将不同的连续长度比较,得到最大连续长度。
1.2 滑动窗口法:比如计算,-1 0 1 1 1 0 1 0 1 -1 1中1重复次数,可以考虑以下滑动过程:
设窗口左右边界,i=0,j=0
遍历j,使j不断右移
当j位置不等于1或者[i,j]区间不全是1,i右移一格。
遍历结束后,得到的[i,j]区间就是最大连续长度,滑动图解如下:
在这里插入图片描述
.
问题2. 给定数组和给定索引,怎么计算索引周围的最大连续长度?比如上面步骤中grids[5]=1时,grids中连续为1的个数是多少?
答:grids[5]的前面有3个1,后面有一个1,如果把grids[5]变为1,那么一共就有5个1

题解

package hwod;

import java.util.Arrays;
import java.util.Scanner;

public class Gobang {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int cur = sc.nextInt();
        sc.nextLine();
        String[] grids = sc.nextLine().split(" ");
        int[] gints = Arrays.stream(grids).mapToInt(Integer::parseInt).toArray();
        System.out.println(getBestIdx(gints, cur));
        System.out.println(getContinueCnt2(gints, cur));
    }


    private static int getBestIdx(int[] gints, int cur) {
        int len = gints.length;
        int origin_max_len = getContinueCnt(gints, cur);
        int bestIdx = -1, mid = len / 2;
        for (int i = 0; i < len; i++) {
            if (gints[i] == 0) {
                int start = i - 1, end = i + 1;
                while (start >= 0 && gints[start] == cur) start--;
                while (end < len && gints[end] == cur) end++;
                int cur_max_len = end - start - 1;
                if (cur_max_len <= 5 && (cur_max_len > origin_max_len || (cur_max_len == origin_max_len && Math.abs(i - mid) < Math.abs(bestIdx - mid)))) {
                    origin_max_len = cur_max_len;
                    bestIdx = i;
                }
            }
        }
        return bestIdx;
    }

    //-1 0 1 1 1 -1 1 -1 1 -1 1
    private static int getContinueCnt(int[] grids, int cur) {
        int ans = 0;
        for (int i = 0; i < grids.length; i++) {
            if (grids[i] == cur) {
                int j = i + 1;
                while (j < grids.length && grids[j] == cur) j++;
                ans = Math.max(ans, j - i);
                i = j;
            }
        }
        return ans;
    }

}

附滑动窗口求最大连续度的方法

    private static int getContinueCnt2(int[] grids, int cur) {
        int[] freq = new int[3]; //记录-1,0,1出现的次数
        int i = 0;
        for (int j = 0; j < grids.length; j++) {
            freq[grids[j] + 1]++;
            if (grids[j] != cur||j-i+1!=freq[cur+1]) {//右边遍历不是cur或者当前区间长度不等于cur出现的次数(即当前区间不全是cur,滑动窗口左边界右移)
                freq[grids[i] + 1]--;
                i++;
            }
        }
        return grids.length - i;
    }

测试用例

用例1
输入:
1
-1 0 1 1 1 0 1 1 0 -1 1
预期结果: 1
结果说明: 网上很多解法,该用例会输出5,但是位置5变为1时,最大连续长度为6,大于5,不满足题意;答案应该是1,此时最大连续长度为4


用例2
输入:
1
1 1 0 1 1 1 1 -1 1 0 1
预期结果:-1
结果说明: 该用例容易错误的输出9,但是题目要求落子后,可以使得当前子的最大连续长度变大,位置9变为1,包含9位置的最大连续长度为3,初始状态的最大连续度为4,并没有变大。因此应该不存在可行位置(位置2倒是能变大,但是那样最大连续长度超过了5,不合题意)

推荐

如果你对本系列的其他题目感兴趣,可以参考华为OD机试真题及题解(JAVA),查看当前专栏更新的所有题目。

说明

本专栏所有文章均为原创,欢迎转载,请注明文章出处:https://blog.csdn.net/qq_31076523/article/details/134176793。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值