2018綠色計算大賽預賽第二階段第三題(TeamBuilding)java
挑戰任務
“綠盟杯”決賽完美落幕之後,賽事團隊組織去一個風景優美的山區進行團建。由於人數眾多必須選擇一塊較大的場地。他們找到了一塊足夠大的地方,但是場地上卻散佈著許多石頭,為了方便活動,必須把這些石頭挪開。現在我們假設整個場地是一塊矩形的地圖,地圖座標的橫縱座標均為非負整數,每個座標點上有一個值:
- 0:代表無法從這個點通過
- 1:代表這個點可以順利通過
- N(大於1):代表這個點上有一個可以去除的石頭,而且石頭的大小是N
現在要求你按照以下規則移除石頭:從當前位置出發,按石頭的大小依次移除石頭,每次先移除從當前位置出發能夠達到的最小的石頭,每移除一個石頭,該石頭所在座標就可以通行即值變為1。你需要編寫一個程式計算出從座標(0,0)出發,移除所有石頭需要走的最小步數,注意,石頭是無法翻越的,而且如果(0,0)上有石頭可以直接移除。如果無法移除所有的石頭就輸出-1。一個溫馨的小提示僅供大家參考,大家注意哦:使用BFS演算法再配以適合的資料結構如PriorityQueue等是一個不錯的選擇!
程式設計要求
補全右側程式碼區中的getMinimumSteps (List<List<Integer>> stones)函式,完成挑戰任務中提出的要求:按照石頭大小,從小到大依次移除場地中的石頭,返回最小的步數,如果無法移除所有的石頭就返回-1。
函式引數說明如下: List<List<Integer>> 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;
}
}