“绿盟杯”决赛完美落幕之后,赛事团队组织去一个风景优美的山区进行团建。由于人数众多必须选择一块较大的场地。他们找到了一块足够大的地方,但是场地上却散布着许多石头,为了方便活动,必须把这些石头挪开。现在我们假设整个场地是一块矩形的地图,地图坐标的横纵坐标均为非负整数,每个坐标点上有一个值:
现在要求你按照以下规则移除石头:从当前位置出发,按石头的大小依次移除石头,每次先移除从当前位置出发能够达到的最小的石头,每移除一个石头,该石头所在坐标就可以通行即值变为1。你需要编写一个程序计算出从坐标(0,0)出发,移除所有石头需要走的最小步数,注意,石头是无法翻越的,而且如果(0,0)上有石头可以直接移除。如果无法移除所有的石头就输出-1。一个温馨的小提示仅供大家参考,大家注意哦:使用BFS算法再配以适合的数据结构如PriorityQueue等是一个不错的选择!
补全右侧代码区中的getMinimumSteps (List> stones)函数,完成挑战任务中提出的要求:按照石头大小,从小到大依次移除场地中的石头,返回最小的步数,如果无法移除所有的石头就返回-1。
函数参数说明如下:
List> stones 以链表形式表示的地场地中各个坐标点的情况(List.get(0).get(0)代表坐标(0,0)),即0代表无法通过,1代表可以顺利通过,大于1的正数代表存在一个可以移除的石头,且石头的大小是该点的值。
你可以根据自己的需求去改动文件,但不要变动已经给出的类名及包名。
样例1:
输入:
1,2,3
0,0,4
7,6,5
输出:
6
样例2:
输入:
1,2,3
0,0,0
7,6,5
输出:
-1
这一道题算是前两个阶段最难的一题,在这道题的时间上花费的时间也是最长。根据题目的意思!!石头不可翻阅!!起初我们认为,存在石头的地方不可通行,只有移除可达到的最小石头,才可以向前走,则问题可以用两个list来解决,一个list存放移除石头的节点,并将节点上的石头数量置为1,另一个list存放的节点是在可通行位置的上、下、左、右满足题目要求的节点。当第二个list不为空时,每次从第二个list中找到石头数量最小的节点,将其放入第一个list,并将这个节点的上、下、左、右节点中满足条件的节点放入第二个list。循环结束后判断stones里还有没有存在节点石头数>1的节点(因为每走过一个有效节点都将其置1)。在这个基础上,我们编写的程序已经可以算出每个输入样例中从第一步到最后一步所经过的节点。代码如下:
import java.util.*;
public class TeamBuilding {
static int[][] nextDirection = {
{0, -1},
{1, 0},
{0, 1},
{-1, 0}
};
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int k=sc.nextInt();
List<List<Integer>>stones=new ArrayList<List<Integer>>();
for(int i=0;i<k;i++){
List<Integer>st=new ArrayList<Integer>();
for(int j=0;j<k;j++){
st.add(sc.nextInt());
}
stones.add(st);
}
for(int i=0;i<k;i++){
for(int j=0;j<k;j++){
System.out.println(stones.get(i).get(j));
}
}
getMinimumSteps(stones);
}
/********* Begin *********/
public static void getMinimumSteps (List<List<Integer>> stones) {
int step = 0;
int start = stones.get(0).get(0);
//if (start == 0) return -1;
//else if (start != 0 && start != 1) start = 1;
boolean flag = true;
List<Node> q=new ArrayList<Node>();
Node tNode = new Node(0, 0, start);
q.add(tNode);
int tLine, tRow;
List<Node> wait=new ArrayList<Node>();
int boo[][]=new int [stones.size()][stones.size()];
for(int i=0;i<stones.size();i++){
for(int j=0;j<stones.size();j++){
boo [i][j]=0;
}
}
wait.add(tNode);
boo[0][0]=1;
Comparator<Node> com=new Comparator<Node>() {
@Override
public int compare(Node o1, Node o2) {
return o1.getP()-o2.getP();
}
};
int gen=1;
while (!wait.isEmpty()) {
Collections.sort(wait,com);
if(gen==1){
wait.remove(0);
List<Integer> s=stones.get(0);
s.set(0,1);
stones.set(0,s);
Node temp = q.get(0);
for (int i = 0; i < 4; i++) {
tLine = temp.getLine() + nextDirection[i][0];
tRow = temp.getRow() + nextDirection[i][1];
if (tLine >= stones.size() || tRow >= stones.size() || tLine < 0 || tRow < 0) {
continue;
}
else if(stones.get(tLine).get(tRow)!=0){
tNode=new Node(tLine,tRow,stones.get(tLine).get(tRow));
if(tNode.getP()!=1||(tNode.getP()==1&&boo[tLine][tRow]!=1)){
wait.add(tNode);
List<Integer> st=stones.get(tLine);
st.set(tRow,1);
stones.set(tLine,st);
boo[tLine][tRow]=1;
}
}
}
}
gen=0;
if(gen!=1){
Node temp=wait.get(0);
q.add(temp);
wait.remove(0);
for (int i = 0; i < 4; i++) {
tLine = temp.line+ nextDirection[i][0];
tRow = temp.row + nextDirection[i][1];
if (tLine >= stones.size() || tRow >= stones.size() || tLine < 0 || tRow < 0) {
continue;
}
else if(stones.get(tLine).get(tRow)!=0){
tNode=new Node(tLine,tRow,stones.get(tLine).get(tRow));
if(tNode.getP()!=1||(tNode.getP()==1&&boo[tLine][tRow]!=1)){
wait.add(tNode);
List<Integer> st=stones.get(tLine);
st.set(tRow,1);
stones.set(tLine,st);
boo[tLine][tRow]=1;
}
}
}
}
}
for (Node t:
q) {
System.out.println("x="+t.getLine()+" "+"y="+t.getRow()+" key="+t.getP());
}
}
public static class Node {
int row, line;
int p;
boolean opened;
public int getP() {
return p;
}
Node(){
}
Node(int line, int row) {
this.line = line;
this.row = row;
}
Node(int line, int row, int p) {
this.row = row;
this.line = line;
this.p = p;
opened=false;
}
public int getRow() {
return row;
}
public int getLine() {
return line;
}
}
/********* End *********/
}
但是,根据题目中未给出但是系统存在的测试用例,我们经过手算都无法得出如此大的结果
输入:
12 34 5 7 8 0
1 0 8 9 12 0
13 0 0 0 11 24
23 32 17 0 0 10
1 2 3 0 0 6
4 8 12 0 0 19
输出:
143
今天,赛事组办方给出的官方答案中,将“石头不可翻越”这一重要条件忽视,根据测试用例以及官方参考代码,石头是可以翻越的,每次找的是全局最小的石头,即如果你在石头为2的地方,移除后,只要存在石头为3的节点,你都要先找到它并移除,不管2-3之间有多少个大于他们的石头,都是可以翻越的。
对于这一解释,我感到十分失望。一个编程竞赛的障碍设置不在程序是否能写出,而在于你对题目的理解。每一个写程序的人都是根据要求一步一步写,生怕因为一个要求没有注意到而没能ac,但是谁曾想主办方给的答案完全与题目意思相悖,并且在比赛中多数人反映的情况下未能给出解释,对于这个比赛有些许的失望。最后附上官方代码
package step3;
import java.util.*;
public class TeamBuilding {
private final int[] dr = {-1, 1, 0, 0};
private final int[] dc = {0, 0, -1, 1};
public int getMinimumSteps(List<List<Integer>> forest) {
List<int[]> trees = new ArrayList<>();
for (int r = 0; r < forest.size(); ++r) {
for (int c = 0; c < forest.get(0).size(); ++c) {
int v = forest.get(r).get(c);
if (v > 1) {
trees.add(new int[]{v, r, c});
}
}
}
Collections.sort(trees, (a, b) -> a[0] - b[0]);
int ans = 0, sr = 0, sc = 0;
for (int[] tree : trees) {
int d = hadlocks(forest, sr, sc, tree[1], tree[2]);
if (d < 0) {
return -1;
}
ans += d;
sr = tree[1];
sc = tree[2];
}
return ans;
}
private int hadlocks(List<List<Integer>> forest, int sr, int sc, int tr, int tc) {
int rows = forest.size(), cols = forest.get(0).size();
Set<Integer> processed = new HashSet<>();
Deque<int[]> deque = new ArrayDeque<>();
deque.offerFirst(new int[]{0, sr, sc});
while (!deque.isEmpty()) {
int[] cur = deque.pollFirst();
int detours = cur[0], r = cur[1], c = cur[2];
if (!processed.contains(r * cols + c)) {
processed.add(r * cols + c);
if (r == tr && c == tc) {
return Math.abs(sr - tr) + Math.abs(sc - tc) + 2 * detours;
}
for (int di = 0; di < 4; ++di) {
int nr = r + dr[di];
int nc = c + dc[di];
boolean closer;
if (di <= 1) {
closer = di == 0 ? r > tr : r < tr;
} else {
closer = di == 2 ? c > tc : c < tc;
}
if (0 <= nr && nr < rows && 0 <= nc && nc < cols && forest.get(nr).get(nc) > 0) {
if (closer) {
deque.offerFirst(new int[]{detours, nr, nc});
} else {
deque.offerLast(new int[]{detours + 1, nr, nc});
}
}
}
}
}
return -1;
}
}