[洛谷P3693]琪露諾的冰雪小屋
阿新 • • 發佈:2018-11-09
題目大意:琪露諾的冰雪小屋,我做的第一道大模擬題。
題解:模擬。。。
卡點:無數小錯誤,要是沒有寫一點調一點,那大概是永遠調不出來了。。。
C++ Code:
#include <cstdio> #include <cstring> #include <queue> #include <cstdlib> #define PutRe(x) {puts(x); return ;} inline int min(int a, int b) {return a < b ? a : b;} inline int max(int a, int b) {return a > b ? a : b;} namespace snow_house { #define MAXN 17 #define MAXH 21 const int __X[8] = {-1, -1, 0, 1, 1, 1, 0, -1}, __Y[8] = {0, -1, -1, -1, 0, 1, 1, 1}; //0.上 1.左上 2.左 3.左下 4.下 5.右下 6.右 7.右上 /* x-1,y-1 x-1,y x-1,y+1 x,y-1 x,y x,y+1 x+1,y-1 x+1,y x+1,y+1 */ int n, HM, m, Tim; int HR, HC, HX, HY, Height; //左上角位置,長度,寬度,房頂高度 int Ice_Block_Num; //剩餘方塊數 int Frozen_Value[MAXN][MAXN]; //地面冷凍值 bool Block[MAXN][MAXN][MAXH]; //是否有方塊 inline bool over_range(int x, int y) {return x < 0 || y < 0 || x >= n || y >= n;} inline bool over_range(int x, int y, int z) {return over_range(x, y) || z < 0 || z > HM;} void ice_barrage(int R, int C, int D, int S) { //發射冰雪彈幕 int k = 0; for (int i = 0; i <= S; i++) { if (Block[R][C][0]) break; if (Frozen_Value[R][C] < 4) Frozen_Value[R][C]++, k++; R += __X[D], C += __Y[D]; if (over_range(R, C)) break; } printf("CIRNO FREEZED %d BLOCK(S)", k); if (Tim != m) puts(""); } void make_ice_block() { //造冰磚 int cnt = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) if (Frozen_Value[i][j] == 4) { Frozen_Value[i][j] = 0; cnt++; } } Ice_Block_Num += cnt; printf("CIRNO MADE %d ICE BLOCK(S),NOW SHE HAS %d ICE BLOCK(S)\n", cnt, Ice_Block_Num); } const int Space_X[6] = {1, -1, 0, 0, 0, 0}, Space_Y[6] = {0, 0, 1, -1, 0, 0}, Space_Z[6] = {0, 0, 0, 0, 1, -1}; bool can_put(int R, int C, int H) { //是否可以放冰磚(即六個方向有冰磚且此位置沒有冰磚) if (Block[R][C][H]) return false; if (H == 0) return true; for (int i = 0; i < 6; i++) { int x = R + Space_X[i], y = C + Space_Y[i], z = H + Space_Z[i]; if (over_range(x, y, z)) continue; if (Block[x][y][z]) return true; } return false; } inline bool outside(int R, int C) {return R < HR || R >= HR + HX || C < HC || C >= HC + HY;} inline bool inside(int R, int C) {return HR + 1 <= R && R <= HR + HX - 2 && HC + 1 <= C && C <= HC + HY - 2;} void put_ice_block(int R, int C, int H) { //放冰磚 if (!Ice_Block_Num) PutRe("CIRNO HAS NO ICE_BLOCK") if (!can_put(R, C, H)) PutRe("BAKA CIRNO,CAN'T PUT HERE") Block[R][C][H] = true; Ice_Block_Num--; if (H == 0) Frozen_Value[R][C] = 0; if (outside(R, C)) PutRe("CIRNO MISSED THE PLACE") if (inside(R, C)) PutRe("CIRNO PUT AN ICE_BLOCK INSIDE THE HOUSE") printf("CIRNO SUCCESSFULLY PUT AN ICE_BLOCK,NOW SHE HAS %d ICE_BLOCK(S)\n", Ice_Block_Num); } struct point { int x, y, z; inline point(int __x = 0, int __y = 0, int __z = 0) {x = __x, y = __y, z = __z;} }; bool vis[MAXN][MAXN][MAXH]; int fall_block(int R, int C, int H) { //計算會掉下來多少個冰磚 if (over_range(R, C, H) || !Block[R][C][H]) return 0; memset(vis, false, sizeof vis); std::queue<point> q; while (!q.empty()) q.pop(); bool Fallen = true; //判斷這個連通塊是否有冰磚接地(即是否會掉下) int cnt = 0; q.push(point(R, C, H)); vis[R][C][H] = true; while (!q.empty()) { point u = q.front(); q.pop(); if (u.z == 0) { Fallen = false; break; } for (int i = 0; i < 6; i++) { int x = u.x + Space_X[i], y = u.y + Space_Y[i], z = u.z + Space_Z[i]; if (over_range(x, y, z) || vis[x][y][z] || !Block[x][y][z]) continue; vis[x][y][z] = true; q.push(point(x, y, z)); } } if (Fallen) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < HM; k++) if (vis[i][j][k]) { Block[i][j][k] = false; cnt++; } } } } return cnt; } void remove_ice_block(int R, int C, int H, bool echo = true) { //移除冰磚,echo:是否輸出(若為false即為造屋頂階段,掉落的方塊會被回收) if (!Block[R][C][H]) { if (echo) PutRe("BAKA CIRNO,THERE IS NO ICE_BLOCK") return ; } Block[R][C][H] = false; Ice_Block_Num++; int k = 0; for (int i = 0; i < 6; i++) k += fall_block(R + Space_X[i], C + Space_Y[i], H + Space_Z[i]); if (!echo) Ice_Block_Num += k; if (echo) { if (!k) puts("CIRNO REMOVED AN ICE_BLOCK"); else printf("CIRNO REMOVED AN ICE_BLOCK,AND %d BLOCK(S) ARE BROKEN\n", k); } } int get_max_height(int x, int y) { //求出該點最高的方塊高度 int res = -1; for (int i = 0; i < HM; i++) if (Block[x][y][i]) res = i; return res; } int get_roof_height() { //求屋頂的高度 int res = -1; for (int i = 0; i < HX; i++) { res = max(res, get_max_height(HR + i, HC)); res = max(res, get_max_height(HR + i, HC + HY - 1)); } for (int i = 1; i < HY - 1; i++) { res = max(res, get_max_height(HR, HC + i)); res = max(res, get_max_height(HR + HX - 1, HC + i)); } return res + 1; } int calc_block_num(int X_1, int Y_1, int X_2, int Y_2, int H) { //計算高度為H的平面中給定區間的方塊數 int res = 0; for (int i = X_1; i <= X_2; i++) { for (int j = Y_1; j <= Y_2; j++) { res += Block[i][j][H]; } } return res; } inline bool outside(int x, int y, int z) {return outside(x, y) || z > Height;} inline bool inside(int x, int y, int z) {return inside(x, y) && z < Height;} int Inside_Block_Num, Outside_Block_Num; void calc_rest_of_block_num() { //計算屋內和屋外的方塊數 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k <= HM; k++) if (Block[i][j][k]) { Inside_Block_Num += inside(i, j, k); Outside_Block_Num += outside(i, j, k); } } } printf("%d ICE_BLOCK(S) INSIDE THE HOUSE NEED TO BE REMOVED\n", Inside_Block_Num); printf("%d ICE_BLOCK(S) OUTSIDE THE HOUSE NEED TO BE REMOVED\n", Outside_Block_Num); } inline bool is_a_part_of_house(int x, int y, int z) {return !inside(x, y, z) && !outside(x, y, z);} void remove_rest_of_block() { //把多餘的方塊移除 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < HM; k++) if (!is_a_part_of_house(i, j, k)) remove_ice_block(i, j, k, false); } } } int Door_X, Door_Y, Door_Max_Value = -1; int mid(int x, int y) { //判斷門是否在牆的中間 if (x == HR || x == HR + HX - 1) { if (HY & 1) return y == HC + (HY >> 1); else return (y == HC + (HY >> 1)) || (y == HC + (HY - 1 >> 1)); } if (y == HC || y == HC + HY - 1) { if (HX & 1) return x == HR + (HX >> 1); else return (x == HR + (HX >> 1)) || (x == HR + (HX - 1 >> 1)); } //出錯 exit(20040826); return -1; } int get_block_num(int x, int y) { //求出(x,y)從地面開始的兩格有幾個方塊 return Block[x][y][0] + Block[x][y][1]; } int need_block_num(int x, int y) { //求出(x,y)從地面開始的兩格還需要幾個方塊 return !Block[x][y][0] + !Block[x][y][1]; } int beside_corner(int x, int y) { //如果門在牆角旁邊,就要多放方塊修牆角(即可以看見),求出原有幾個方塊 int res = 0; bool Is_Beside_Corner = false; if (x - 1 == HR) res += get_block_num(x - 1, y), Is_Beside_Corner = true; if (x + 1 == HR + HX - 1) res += get_block_num(x + 1, y), Is_Beside_Corner = true; if (y - 1 == HC) res += get_block_num(x, y - 1), Is_Beside_Corner = true; if (y + 1 == HC + HY - 1) res += get_block_num(x, y + 1), Is_Beside_Corner = true; if (!Is_Beside_Corner) res = 4; return res; } int door_value(int x, int y) { //求出在此建門的優先順序 //BBmccc return need_block_num(x, y) << 4 | mid(x, y) << 3 | beside_corner(x, y); } void get_max(int x, int y) { //更新門的位置 int Value = door_value(x, y); if (Value > Door_Max_Value) Door_X = x, Door_Y = y, Door_Max_Value = Value; } void find_door() { //找到建門的最好地點 for (int i = HR + 1; i < HR + HX - 1; i++) { get_max(i, HC); get_max(i, HC + HY - 1); } for (int i = HC + 1; i < HC + HY - 1; i++) { get_max(HR, i); get_max(HR + HX - 1, i); } } #define fix(x, y, z) cnt += !Block[x][y][z], Block[x][y][z] = true #define fix_double(x, y) cnt += need_block_num(x, y), Block[x][y][0] = Block[x][y][1] = true bool is_door(int x, int y, int z) { //判斷這個位置是否是門 return x == Door_X && y == Door_Y && z < 2; } int fix_wall() { //修牆,並求出需要的方塊數 int cnt = 0; for (int i = HR + 1; i < HR + HX - 1; i++) { for (int j = 0; j < Height; j++) { if (!is_door(i, HC, j)) fix(i, HC, j); if (!is_door(i, HC + HY - 1, j)) fix(i, HC + HY - 1, j); } } for (int i = HC + 1; i < HC + HY - 1; i++) { for (int j = 0; j < Height; j++) { if (!is_door(HR, i, j)) fix(HR, i, j); if (!is_door(HR + HX - 1, i, j)) fix(HR + HX - 1, i, j); } } if (Door_X - 1 == HR) fix_double(Door_X - 1, Door_Y); if (Door_X + 1 == HR + HX - 1) fix_double(Door_X + 1, Door_Y); if (Door_Y - 1 == HC) fix_double(Door_X, Door_Y - 1); if (Door_Y + 1 == HC + HY - 1) fix_double(Door_X, Door_Y + 1); return cnt; } int fix_corner() { //修牆角,並求出需要的方塊數 int cnt = 0; for (int i = 0; i < Height; i++) { fix(HR, HC, i); fix(HR, HC + HY - 1, i); fix(HR + HX - 1, HC, i); fix(HR + HX - 1, HC + HY - 1, i); } return cnt; } void make_roof() { //造屋頂 Height = get_roof_height(); int Make_Roof_Need = HX * HY - calc_block_num(HR, HC, HR + HX - 1, HC + HY - 1, Height); if (Ice_Block_Num < Make_Roof_Need) PutRe("SORRY CIRNO,NOT ENOUGH ICE_BLOCK(S) TO MAKE ROOF") if (Height < 2 || HX <= 2 || HY <= 2) PutRe("SORRY CIRNO,HOUSE IS TOO SMALL"); Ice_Block_Num -= Make_Roof_Need; for (int i = HR; i < HR + HX; i++) { for (int j = HC; j < HC + HY; j++) Block[i][j][Height] = true; } calc_rest_of_block_num(); remove_rest_of_block(); if (!Block[HR][HC][Height]) PutRe("SORRY CIRNO,HOUSE IS BROKEN WHEN REMOVING BLOCKS") find_door(); int Fix_Wall_Need = fix_wall(); if (Ice_Block_Num < Fix_Wall_Need) PutRe("SORRY CIRNO,NOT ENOUGH ICE_BLOCKS TO FIX THE WALL") Ice_Block_Num -= Fix_Wall_Need; puts("GOOD JOB CIRNO,SUCCESSFULLY BUILT THE HOUSE"); if (Door_Max_Value >> 4 == 2) puts("DOOR IS OK"); else { puts("HOUSE HAS NO DOOR"); Ice_Block_Num += Block[Door_X][Door_Y][0] + Block[Door_X][Door_Y][1]; Block[Door_X][Door_Y][0] = Block[Door_X][Door_Y][1] = false; } if (!Fix_Wall_Need) puts("WALL IS OK"); else puts("WALL NEED TO BE FIXED"); int Fix_Corner_Need = fix_corner(); if (Ice_Block_Num < Fix_Corner_Need) Ice_Block_Num = 0; else Ice_Block_Num -= Fix_Corner_Need; if (!Fix_Corner_Need) puts("CORNER IS OK"); else puts("CORNER NEED TO BE FIXED"); printf("CIRNO FINALLY HAS %d ICE_BLOCK(S)\n", Ice_Block_Num); if (!Inside_Block_Num && !Outside_Block_Num && !Fix_Wall_Need && !Fix_Corner_Need && (Door_Max_Value & 1 << 3) && (Door_Max_Value >> 4 == 2)) puts("CIRNO IS PERFECT!"); } void start() { scanf("%d", &n); scanf("%d", &HM); scanf("%d%d%d%d", &HR, &HC, &HX, &HY); scanf("%d", &m); char op[20]; for (Tim = 1; Tim <= m; Tim++) { scanf("%s", op); if (strcmp(op, "ICE_BARRAGE") == 0) { int R, C, D, S; scanf("%d%d%d%d", &R, &C, &D, &S); ice_barrage(R, C, D, S); continue; } if (strcmp(op, "MAKE_ICE_BLOCK") == 0) { make_ice_block(); continue; } if (strcmp(op, "PUT_ICE_BLOCK") == 0) { int R, C, H; scanf("%d%d%d", &R, &C, &H); put_ice_block(R, C, H); continue; } if (strcmp(op, "REMOVE_ICE_BLOCK") == 0) { int R, C, H; scanf("%d%d%d", &R, &C, &H); remove_ice_block(R, C, H); } if (strcmp(op, "MAKE_ROOF") == 0) { make_roof(); } } } } int main() { snow_house::start(); return 0; }