(B卷,100分)- 五子棋迷(Java & JS & Python & C)

题目描述

张兵和王武是五子棋迷,工作之余经常切磋棋艺。这不,这会儿又下起来了。走了一会儿,轮张兵了,对着一条线思考起来了,这条线上的棋子分布如下:
用数组表示: -1 0 1 1 1 0 1 0 1 -1
棋了分布说明:

  • -1代表白子,0代表空位,1 代表黑子
  • 数组长度L,满足 1 < L < 40,L为奇数

你得帮他写一个程序,算出最有利的出子位置。 最有利定义:

  • 找到一个空位(0),用棋子(1/-1)填充该位置,可以使得当前子的最大连续长度变大
  • 如果存在多个位置,返回最靠近中间的较小的那个坐标
  • 如果不存在可行位置,直接返回-1
  • 连续长度不能超过5个(五字棋约束)

输入描述

第一行: 当前出子颜色

第二行: 当前的棋局状态

输出描述

1个整数,表示出子位置的数组下标

用例

输入 1
-1 0 1 1 1 0 1 -1 1
输出 5
说明 当前为黑子 (1),放置在下标为5的位置,黑子的最大连续长度,可以由3到5
输入 -1
-1 0 1 1 1 0 1 0 1 -1 1
输出 1
说明 当前为白子,唯一可以放置的位置下标为1,白子的最大长度,由1变为2
输入 1
0 0 0 0 1 0 0 0 0 1 0
输出 5
说明 可行的位置很多,5最接近中间的位置坐标

题目解析

本题可以使用双指针解题。

定义两个指针L,R,我们假设L,R范围就是要求的连棋范围,那么L,R范围内必须要包含一个0,用于落子,且只能有一个0,范围内其余棋子必须是下棋者对应的颜色(第一行输入的颜色)。

另外,根据题目描述:

连续长度不能超过5个(五字棋约束)

即L,R范围内需要满足三个条件:

  • L,R范围内必须要包含一个0,用于落子,且只能有一个0
  • 范围内其余棋子必须是下棋者对应的颜色
  • L,R范围长度不能超过5

上面三个条件约束着双指针的运动,下面给出三个用例的L,R指针运动示意图:

用例1

通过上面例子,我们可以知道,在什么时机进行连棋的统计,需要统计连棋的落子位置和长度

用例2

上面例子中,如果连棋中断,即遇到不同颜色的棋子,则L,R需要同时移动到该不同颜色棋子的右侧

用例3

 上面例子中,如果落子超标,则我们需要将L指针移动到上一次落子位置的右侧,以此来满足L,R范围内落子数量只有一个


2023.06.04

上面逻辑没有考虑到一个点:

找到一个空位(0),用棋子(1/-1)填充该位置,可以使得当前子的最大连续长度变大

即落子后,一定要让当前子的最大连续长度变大,如果落子后,该子的最大连续长度没有变大,那么就代表不是最有利位置

因此,下面代码追加了一个getInitMaxConstantLen方法,用于获取初始状态时的最大连续长度,后续落子时,需要看新的连续长度是否超过初始最大的,只有超过了,对应落子位置才是一个可能解。

Java算法源码

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

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

    int color = Integer.parseInt(sc.nextLine());
    int[] nums = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();

    System.out.println(getResult(color, nums));
  }

  public static int getResult(int color, int[] nums) {
    // 获取初始的最大连续长度
    int initMaxConstantLen = getInitMaxConstantLen(color, nums);

    ArrayList<int[]> ans = new ArrayList<>();

    // l~r之间必须且只能包含一个0,即必须落子一次,其余都是color颜色的棋子
    int l = 0;
    int r = 0;

    // l~r之间包含的0的数量,即落子数量
    int zero = 0;
    // l~r之间0的位置,即落子位置
    int pos = -1;

    while (r < nums.length) {
      // 如果nums[r]是空位
      if (nums[r] == 0) {
        // 则可以落子,因此落子个数++
        zero++;

        // 如果落子数量超过1个了,则此时 l~r-1 范围就是一个连棋(PS:r位置不算在内),
        // 如果该连棋长度 (r-1) - l + 1 <= 5(PS:五字棋约束),则是一个合法的连棋
        // 本题要求落子可以使得当前子的最大连续长度变大
        if (zero > 1 && r - l <= 5 && r - l > initMaxConstantLen) {
          ans.add(new int[] {pos, r - l}); // 记录 l~r-1 范围的落子位置pos,以及连续长度r-l
        }

        // 由于只能落子一次,因此前面的落子需要收回,即更新 l 到上一次落子位置的右边
        if (zero > 1) {
          zero--;
          l = pos + 1;
        }

        // 更新落子位置
        pos = r;

        ++r;
      }
      // 如果nums[r]位置有其他颜色棋子,则连棋中断
      else if (nums[r] != color) {
        // 此时需要检查 l~r-1 范围是否落过子,且是否符合五子棋约束
        // 若是,则记录 l~r-1 范围的落子位置pos,以及连续长度r-l
        // 本题要求落子可以使得当前子的最大连续长度变大
        if (zero == 1 && r - l <= 5 && r - l > initMaxConstantLen) ans.add(new int[] {pos, r - l});
        // 由于连棋中断了,因此落子位置pos,和落子数量全部重置
        pos = -1;
        zero = 0;
        // l,r全部更新到当前r的右边一个位置
        l = ++r;
      }
      // 如果nums[r]位置有当前颜色棋子,则连棋继续
      else {
        ++r;
      }
    }

    // 收尾操作
    if (zero == 1 && r - l <= 5 && r - l > initMaxConstantLen) {
      ans.add(new int[] {pos, r - l});
    }

    // 如果没有统计到连棋情,则返回-1
    if (ans.size() == 0) return -1;

    int mid = nums.length / 2;

    // 如果统计到连棋
    // 先按照连棋长度降序,如果长度相同,则按照接近中心位置mid的距离升序(越近的越优),如果距离中心位置mid相同,则按照落子位置升序(越小的越优)
    ans.sort((a, b) -> a[1] != b[1] ? b[1] - a[1] : cmp(a[0], b[0], mid));
    return ans.get(0)[0]; // 取最优情况的落子位置
  }

  // 比较pos1,pos2谁更接近mid,如果距离mid相同,则返回较小的
  public static int cmp(int pos1, int pos2, int mid) {
    int dis1 = Math.abs(pos1 - mid);
    int dis2 = Math.abs(pos2 - mid);

    if (dis1 != dis2) {
      return dis1 - dis2;
    } else {
      return pos1 - pos2;
    }
  }

  // 获取初始最大连续长度,即未落子前的最大连续长度
  public static int getInitMaxConstantLen(int color, int[] nums) {
    int maxLen = 0;

    int len = 0;
    for (int num : nums) {
      if (num != color) {
        maxLen = Math.max(maxLen, len);
        len = 0;
      } else {
        len++;
      }
    }

    return Math.max(maxLen, len);
  }
}

JS算法源码

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

const lines = [];
rl.on("line", (line) => {
  lines.push(line);

  if (lines.length == 2) {
    const color = lines[0] - 0;
    const nums = lines[1].split(" ").map(Number);

    console.log(getResult(color, nums));

    lines.length = 0;
  }
});

function getResult(color, nums) {
  // 获取初始的最大连续长度
  const initMaxConstantLen = getInitMaxConstantLen(color, nums);

  const ans = [];

  // l~r之间必须落子一次,其余都是color颜色的棋子
  let l = 0;
  let r = 0;

  // l~r之间包含的0的数量,即落子数量
  let zero = 0;
  // l~r之间0的位置,即落子位置
  let pos = -1;

  while (r < nums.length) {
    // 如果nums[r]是空位
    if (nums[r] == 0) {
      // 则可以落子,因此落子个数++
      zero++;

      // 如果落子数量超过1个了,则此时 l~r-1 范围就是一个连棋(PS:r位置不算在内),
      // 如果该连棋长度 (r-1) - l + 1 <= 5(PS:五字棋约束),则是一个合法的连棋
      // 本题要求落子可以使得当前子的最大连续长度变大
      if (zero > 1 && r - l <= 5 && r - l > initMaxConstantLen) {
        // 记录 l~r-1 范围的落子位置pos,以及连续长度r-l
        ans.push([pos, r - l]);
      }

      // 由于只能落子一次,因此前面的落子需要收回,即更新 l 到上一次落子位置的右边
      if (zero > 1) {
        zero--;
        l = pos + 1;
      }

      // 更新落子位置
      pos = r;

      ++r;
    }
    // 如果nums[r]位置有其他颜色棋子,则连棋中断
    else if (nums[r] != color) {
      // 此时需要检查 l~r-1 范围是否落过子,且是否符合五子棋约束
      // 若是,则记录 l~r-1 范围的落子位置pos,以及连续长度r-l
      // 本题要求落子可以使得当前子的最大连续长度变大
      if (zero == 1 && r - l <= 5 && r - l > initMaxConstantLen) {
        ans.push([pos, r - l]);
      }
      // 由于连棋中断了,因此落子位置pos,和落子数量全部重置
      pos = -1;
      zero = 0;
      // l,r全部更新到当前r的右边一个位置
      l = ++r;
    }
    // 如果nums[r]位置有当前颜色棋子,则连棋继续
    else {
      ++r;
    }
  }

  // 收尾操作
  if (zero == 1 && r - l <= 5 && r - l > initMaxConstantLen) {
    ans.push([pos, r - l]);
  }

  // 如果没有统计到连棋情,则返回-1
  if (ans.length == 0) return -1;

  const mid = Math.floor(nums.length / 2);

  // 如果统计到连棋
  // 先按照连棋长度降序,如果长度相同,则按照接近中心位置mid的距离升序(越近的越优),如果距离中心位置mid相同,则按照落子位置升序(越小的越优)
  ans.sort((a, b) => (a[1] != b[1] ? b[1] - a[1] : cmp(a[0], b[0], mid)));

  return ans[0][0]; // 取最优情况的落子位置
}

// 比较pos1,pos2谁更接近mid,如果距离mid相同,则返回较小的pos
function cmp(pos1, pos2, mid) {
  const dis1 = Math.abs(pos1 - mid);
  const dis2 = Math.abs(pos2 - mid);

  if (dis1 != dis2) {
    return dis1 - dis2;
  } else {
    return pos1 - pos2;
  }
}

function getInitMaxConstantLen(color, nums) {
  let maxLen = 0;

  let len = 0;
  for (let num of nums) {
    if (num != color) {
      maxLen = Math.max(maxLen, len);
      len = 0;
    } else {
      len++;
    }
  }

  return Math.max(maxLen, len);
}

Python算法源码

# 输入获取
color = int(input())
nums = list(map(int, input().split()))


# 获取初始最大连续长度,即未落子前的最大连续长度
def getInitMaxConstantLen():
    maxLen = 0

    length = 0
    for num in nums:
        if num != color:
            maxLen = max(maxLen, length)
            length = 0
        else:
            length += 1

    return max(maxLen, length)


# 算法入口
def getResult():
    # 获取初始的最大连续长度
    initMaxConstantLen = getInitMaxConstantLen()

    ans = []

    # l~r之间必须且只能包含一个0,即必须落子一次,其余都是color颜色的棋子
    l = 0
    r = 0

    # l~r之间包含的0的数量,即落子数量
    zero = 0
    # l~r之间0的位置,即落子位置
    pos = -1

    while r < len(nums):
        # 如果nums[r]是空位
        if nums[r] == 0:
            # 则可以落子,因此落子个数++
            zero += 1

            # 如果落子数量超过1个了,则此时 l~r-1 范围就是一个连棋(PS:r位置不算在内)
            if zero > 1:
                # 如果该连棋长度 (r-1) - l + 1 <= 5(PS:五字棋约束),则是一个合法的连棋
                # 记录 l~r-1 范围的落子位置pos,以及连续长度r-l
                # 本题要求落子可以使得当前子的最大连续长度变大
                if initMaxConstantLen < r - l <= 5:
                    ans.append([pos, r - l])

                # 由于只能落子一次,因此前面的落子需要收回,即更新 l 到上一次落子位置的右边
                zero -= 1
                l = pos + 1

            # 更新落子位置
            pos = r
            r += 1
        # 如果nums[r]位置有其他颜色棋子,则连棋中断
        elif nums[r] != color:
            # 此时需要检查 l~r-1 范围是否落过子,且是否符合五子棋约束
            # 若是,则记录 l~r-1 范围的落子位置pos,以及连续长度r-l
            # 本题要求落子可以使得当前子的最大连续长度变大
            if zero == 1 and initMaxConstantLen < r - l <= 5:
                ans.append([pos, r - l])

            # 由于连棋中断了,因此落子位置pos,和落子数量全部重置
            pos = -1
            zero = 0

            # l,r全部更新到当前r的右边一个位置
            r += 1
            l = r
        else:
            r += 1

    # 收尾操作
    if zero == 1 and initMaxConstantLen < r - l <= 5:
        ans.append([pos, r - l])

    # 如果没有统计到连棋情,则返回-1
    if len(ans) == 0:
        return -1

    mid = len(nums) // 2

    # 如果统计到连棋
    # 先按照连棋长度降序,如果长度相同,则按照接近中心位置mid的距离升序(越近的越优),如果距离中心位置mid相同,则按照落子位置升序(越小的越优)
    ans.sort(key=lambda x: (-x[1], abs(x[0] - mid), x[0]))

    # 取最优情况的落子位置
    return ans[0][0]


# 算法调用
print(getResult())

C算法源码

#include <stdio.h>
#include <stdlib.h>

// 动态数组 数据结构定义
typedef struct List {
    int** array;
    int capacity;
    int size;
} ArrayList;

// 动态数组的底层容器的初始容量
#define MIN_CAPACITY 50

// 创建动态数组
ArrayList* new_ArrayList();
// 向动态数组加入元素
void add_ArrayList(ArrayList* list, int* ele);

// 算法代码
#define MAX_SIZE 40
#define MAX(a,b) (a > b ? a : b)

int getResult();
int getInitMaxConstantLen();
int cmp(const void* a, const void* b);

int color;
int nums[MAX_SIZE];
int nums_size = 0;

int main() {
    // 输入获取
    scanf("%d", &color);

    while(scanf("%d", &nums[nums_size++])) {
        if(getchar() != ' ') break;
    }

    // 算法入口调用
    printf("%dn", getResult());

    return 0;
}

int getResult() {
    // 获取初始的最大连续长度
    int initMaxConstantLen = getInitMaxConstantLen();

    ArrayList* list = new_ArrayList();

    // l~r之间必须且只能包含一个0,即必须落子一次,其余都是color颜色的棋子
    int l = 0;
    int r = 0;

    // l~r之间包含的0的数量,即落子数量
    int zero = 0;
    // l~r之间0的位置,即落子位置
    int pos = -1;

    while(r < nums_size) {
        int* tmp = (int*) malloc(sizeof(int) * 2);
        tmp[0] = pos;
        tmp[1] = r - l;

        // 如果nums[r]是空位
        if(nums[r] == 0) {
            // 则可以落子,因此落子个数++
            zero++;

            // 如果落子数量超过1个了,则此时 l~r-1 范围就是一个连棋(PS:r位置不算在内),
            // 如果该连棋长度 (r-1) - l + 1 <= 5(PS:五字棋约束),则是一个合法的连棋
            // 本题要求落子可以使得当前子的最大连续长度变大
            if(zero > 1 && r - l <= 5 && r - l > initMaxConstantLen) {
                add_ArrayList(list, tmp); // 记录 l~r-1 范围的落子位置pos,以及连续长度r-l
            }

            // 由于只能落子一次,因此前面的落子需要收回,即更新 l 到上一次落子位置的右边
            if(zero > 1) {
                zero--;
                l = pos + 1;
            }

            // 更新落子位置
            pos = r;

            ++r;
        }
        // 如果nums[r]位置有其他颜色棋子,则连棋中断
        else if(nums[r] != color) {
            // 此时需要检查 l~r-1 范围是否落过子,且是否符合五子棋约束
            // 若是,则记录 l~r-1 范围的落子位置pos,以及连续长度r-l
            // 本题要求落子可以使得当前子的最大连续长度变大
            if(zero == 1 && r - l <= 5 && r - l > initMaxConstantLen) {
                add_ArrayList(list, tmp);
            }

            // 由于连棋中断了,因此落子位置pos,和落子数量全部重置
            pos = -1;
            zero = 0;

            // l,r全部更新到当前r的右边一个位置
            l = ++r;
        }
        // 如果nums[r]位置有当前颜色棋子,则连棋继续
        else {
            ++r;
        }
    }

    // 收尾操作
    if(zero == 1 && r - l <= 5 && r - l > initMaxConstantLen) {
        int* tmp = (int*) malloc(sizeof(int) * 2);
        tmp[0] = pos;
        tmp[1] = r - l;

        add_ArrayList(list, tmp);
    }

    // 如果没有统计到连棋情,则返回-1
    if(list->size == 0) {
        return -1;
    }

    // 如果统计到连棋
    // 先按照连棋长度降序,如果长度相同,则按照接近中心位置mid的距离升序(越近的越优),如果距离中心位置mid相同,则按照落子位置升序(越小的越优)
    qsort(list->array, list->size, sizeof(int*), cmp);

    return list->array[0][0]; // 取最优情况的落子位置
}

// 获取初始最大连续长度,即未落子前的最大连续长度
int getInitMaxConstantLen() {
    int maxLen = 0;

    int len = 0;
    for(int i=0; i<nums_size; i++) {
        int num = nums[i];

        if(num != color) {
            maxLen = MAX(maxLen, len);
            len = 0;
        } else {
            len++;
        }
    }

    return MAX(maxLen, len);
}


int cmp(const void* a, const void* b) {
    int* A = *((int**) a);
    int* B = *((int**) b);

    if(A[1] != B[1]) {
        return B[1] - A[1];
    }

    int mid = nums_size / 2;

    // 比较pos1,pos2谁更接近mid,如果距离mid相同,则返回较小的
    int dis1 = abs(A[0] - mid);
    int dis2 = abs(B[0] - mid);

    if(dis1 != dis2) {
        return dis1 - dis2;
    } else {
        return A[0] - B[0];
    }
}

ArrayList* new_ArrayList() {
    ArrayList* list = (ArrayList*) malloc(sizeof(ArrayList));

    list->array = (int**) malloc(sizeof(int*) * MIN_CAPACITY);
    list->capacity = MIN_CAPACITY;
    list->size = 0;

    return list;
}

void add_ArrayList(ArrayList* list, int* ele) {
    if(list->size >= list->capacity) {
        list->capacity <<= 1;

        void* p = realloc(list->array, sizeof(int*) * list->capacity);

        if(p == NULL) {
            exit(1);
        }

        list->array = (int**) p;
    }

    list->array[list->size] = ele;
    list->size++;
}

免责声明:

1、IT资源小站为非营利性网站,全站所有资料仅供网友个人学习使用,禁止商用
2、本站所有文档、视频、书籍等资料均由网友分享,本站只负责收集不承担任何技术及版权问题
3、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除下载链接并致以最深的歉意
4、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
5、一经注册为本站会员,一律视为同意网站规定,本站管理员及版主有权禁止违规用户
6、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和IT资源小站的同意
7、IT资源小站管理员和版主有权不事先通知发贴者而删除本文

0

评论0

站点公告

没有账号?注册  忘记密码?