AcWing 674.超级2048
題目鏈接:?674. 超級2048 - AcWing題庫
題目描述:
2048?是一個著名的單人游戲,其目標是在網格上滑動圖塊以組合它們并創建數字為2048的圖塊。
2048?在一個簡單的4×4?網格上進行游戲,其中有一些圖塊,玩家可以對它們進行移動。
每一步操作,玩家可以選擇向左,向右,向上和向下?4?個方向去移動所有圖塊。
如果兩個包含數字相同的圖塊在移動時發生碰撞,則它們將合并為一個圖塊,該圖塊上的數值為兩個圖塊的數字之和。
在一次操作中,一個新創建的圖塊不能再次參與合并,并且圖塊們總是優先沿著移動方向與其旁邊的圖塊進行合并。
例如,如果三個?2?在一行構成?2 2 2,這時玩家選擇向左移動,則它將變為?4 2 0,最左邊的兩個?22?會被合并。
上圖顯示了當玩家將所有圖塊“右移”時?4×4?網格的變化情況。
愛麗絲和鮑勃非常喜愛這個游戲。
但是幾輪后,他們覺得網格的尺寸太小了并不刺激,所以決定將網格的尺寸擴大到?N×NN×N,他們稱之為“超級?2048”游戲。
但是難度的激增讓他們無法駕馭這個游戲。
他們要求你編寫一個程序來幫助他們弄清楚在所有圖塊沿著某個特定方向移動后網格的樣子。
輸入格式
第一行包含整數?T,表示共有?T?組測試數據。
每組數據第一行包含整數?N?和一個字符串?DIR,其中?NN?表示網格的尺寸,DIR?表示圖塊的移動方向,DIR?將會是以下四個字符串中的一個:left,?right,?up,?down。
接下來?N?行,每行包含?N?個整數,用來描述網格的初始狀態,每個整數表示網格中一個圖塊的值,如果為?0?則表示該位置是空的。
輸出格式
對于每組測試數據,第一行輸出?Case #x:,其中?x?為組別編號(從?1?開始)。
然后輸出?N?行,每行?N?個空格隔開的整數,表示圖塊移動后網格的樣子。
數據范圍
1≤T≤100
1≤N≤20
網格中的每個數字都是?0?或?2?到?1024?之間的?2?的整數次冪,包括?2?和?1024。
輸入樣例:
3 4 right 2 0 2 4 2 0 4 2 2 2 4 8 2 2 4 4 10 up 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 3 right 2 2 2 4 4 4 8 8 8輸出樣例:
Case #1: 0 0 4 4 0 2 4 2 0 4 4 8 0 0 4 8 Case #2: 4 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Case #3: 0 2 4 0 4 8 0 8 16思想
(此距離為右向移動,圖片丑請勿怪):
?定義start下標指針,指向遍歷的起點,idx初始值為遍歷的開始起點也就是start的初始值
由start開始遍歷,由新的 k 下標指針(start - 1),進行遍歷到start當前位置之后(此“之后為遍歷的下一個位置的意思),當遇到不為0的時候停止。查看當前 k 和 start指向值的關系,如果是相同的那么也就發生了“碰撞”行為(2048游戲中移動時相鄰的相同值會被合并,以下我簡稱”碰撞“),那么將 idx 指向的下標位置的值賦值為 當前 start 值指向的兩倍、如果沒有發生”碰撞“,那么將?idx 指向的下標位置的值賦值為 當前 start 值,隨后,將 start 的下標位置挪動到當前 k 下標所指向的位置。此圖為第一次遍歷之后所產生的情況。
當最后一次遍歷之后會是下圖的情況
?完成之后,在以idx為遍歷起點,將之后的值初始化為‘空’(0值),即可
先是最為初始化的思想,將上下左右的移動,寫出為四個函數:
之后將是把四個情況融合在一起:
#include <bits/stdc++.h> using namespace std;const int N = 30; int arr[N][N];void getUp(int n); void getDown(int n); void getLeft(int n); void getRight(int n);int main() {int t;cin >> t;for (int t1 = 1;t1 <= t;t1 ++) {int n;string str;cin >> n >> str;for (int i = 0;i < n;i ++) for (int j = 0;j < n;j ++) cin >> arr[i][j];if (str == "up") getUp(n);if (str == "down") getDown(n);if (str == "left") getLeft(n);if (str == "right") getRight(n);cout << "Case #" << t1 << ":" << endl;for (int i = 0;i < n;i ++) {for (int j = 0;j < n;j ++) cout << arr[i][j] << ' ';cout << endl;}}return 0; }void getUp(int n){for (int i = 0;i < n;i ++) { // 遍歷每一列int idx = 0;for (int j = 0;j < n;j ++) {if (arr[j][i] == 0) continue; // j => 行號int k = j + 1;while (k < n && arr[k][i] == 0) k ++;if (k < n && arr[j][i] == arr[k][i]) {arr[idx ++][i] = 2 * arr[j][i];j = k;}else arr[idx ++][i] = arr[j][i];}for (int j = idx;j < n;j ++) arr[j][i] = 0;} }void getDown(int n){for (int i = 0;i < n;i ++) { // 遍歷每一列int idx = n - 1;for (int j = n - 1;j >= 0;j --) {if (arr[j][i] == 0) continue; // j => 行號int k = j - 1;while (k >= 0 && arr[k][i] == 0) k --;if (k >= 0 && arr[j][i] == arr[k][i]) {arr[idx --][i] = 2 * arr[j][i];j = k;}else arr[idx --][i] = arr[j][i];}for (int j = idx;j >= 0;j --) arr[j][i] = 0;} }void getLeft(int n){for (int i = 0;i < n;i ++) {int idx = 0;for (int j = 0;j < n;j ++) {if (arr[i][j] == 0) continue;int k = j + 1;while (k < n && arr[i][k] == 0) k ++;if (k < n && arr[i][j] == arr[i][k]) {arr[i][idx ++] = 2 * arr[i][k];j = k;}else arr[i][idx ++] = arr[i][j];}for (int j = idx;j < n;j ++) arr[i][j] = 0;} }void getRight(int n){for (int i = 0;i < n;i ++) {int idx = n - 1;for (int j = idx;j >= 0;j --) {if (arr[i][j] == 0) continue;int k = j - 1;while (k >= 0 && arr[i][k] == 0) k --;if (k >= 0 && arr[i][j] == arr[i][k]) {arr[i][idx --] = 2 * arr[i][k];j = k;}else arr[i][idx --] = arr[i][j];}for (int j = idx;j >= 0;j --) arr[i][j] = 0;} }將四種情況寫到了一種函數當中:
#include <bits/stdc++.h> using namespace std;const int N = 30; int arr[N][N];void check(int n, int fleft, int fright);int main() {int t;cin >> t;for (int t1 = 1;t1 <= t;t1 ++) {int n;string str;cin >> n >> str;for (int i = 0;i < n;i ++) for (int j = 0;j < n;j ++) cin >> arr[i][j];if (str == "up") check(n, 0, 1);if (str == "down") check(n, 0, -1);if (str == "left") check(n, 1, 0);if (str == "right") check(n, -1, 0);cout << "Case #" << t1 << ":" << endl;for (int i = 0;i < n;i ++) {for (int j = 0;j < n;j ++) cout << arr[i][j] << ' ';cout << endl;}}return 0; }/* 解決2048游戲中橫豎向移動問題, 其中arr是存儲為int類型的2048圖@parma n 當前移動位置的長度, 默認區間在[0, n-1]中@parma frow 當前是否橫向移動, frow > 0 => 向右移動, frow < 0 => 向左移動, frow == 0 不移動@parma fcol 當前是否縱向移動, fcol > 0 => 向下移動, fcol < 0 => 向上移動, fcol == 0 不移動 */ void check(int n, int frow, int fcol) {// 判斷當前是否出現了不移動,或者橫縱向同時移動的狀態,此為錯誤的操作if ((frow == 0 && fcol == 0) || (frow != 0 && fcol != 0)) return;int start, flag; // 定義開始遍歷的起點下標, 遍歷的方向// 當flag = 1時候,即為從左往右(從上往下)開始遍歷, 為-1的時候就相反// ----------------相應的start起點下標也就是0,----------------n - 1if (frow > 0 || fcol > 0) start = 0, flag = 1;else start = n - 1, flag = -1;for (int i = 0;i < n;i ++) { // 開始橫線或縱向遍歷//定義一個和start同向且初始值相同的下標指針,當2048開始移動時發生碰撞產生改變時//存儲點的下標位置int idx = start; //開始縱向或橫線遍歷,這取決外層循環的涵義for (int j = start;j >= 0 && j < n;j += flag) {//當frow != 0,表示現在外層循環是一個縱向遍歷,內層循環是一個橫向遍歷…………//即在遍歷過程中遇到0,也就是2048游戲中的空位的時候,進行一個跳過if ((frow != 0 && arr[i][j] == 0) || (fcol != 0 && arr[j][i] == 0)) continue;//定義k指針為當前j指針的后一個(此前并非為j + 1,而是遍歷元素的后一個,與遍歷方向有關int k = j + flag;//用于跳過j指針后存在的'空'(0值),直到找到相同值或不同值if (fcol != 0) while (k >= 0 && k < n && arr[k][i] == 0) k += flag;//縱向遍歷else while (k >= 0 && k < n && arr[i][k] == 0) k += flag;//橫向遍歷//找到需要處理的值之后,判斷橫縱向遍歷if (frow == 0) {//如果當前 k 指針沒有越界行為,并且與 j 指針指向的值相同,即發生碰撞if (k >= 0 && k < n && arr[j][i] == arr[k][i]) arr[idx][i] = 2 * arr[j][i], j = k;else arr[idx][i] = arr[j][i];// 有不同值則賦值到存儲點}else {if (k >= 0 && k < n && arr[i][j] == arr[i][k]) arr[i][idx] = 2 * arr[i][j], j = k;else arr[i][idx] = arr[i][j];}//處理完一次碰撞或沒有碰撞之后,存儲點的位置也要跟隨start遍歷的方向挪動idx += flag;}//移動處理完之后,剩余的部分理應為'空'(0值)for (int j = idx;j >= 0 && j < n;j += flag)//判斷橫縱向遍歷,進行賦值frow == 0 ? arr[j][i] = 0 : arr[i][j] = 0;} }總結
以上是生活随笔為你收集整理的AcWing 674.超级2048的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Zotero IEEE trans期刊c
- 下一篇: SVN branch分支管理