1. 程式人生 > >[洛谷P3693]琪露諾的冰雪小屋

[洛谷P3693]琪露諾的冰雪小屋

題目大意:琪露諾的冰雪小屋,我做的第一道大模擬題。

題解:模擬。。。

卡點:無數小錯誤,要是沒有寫一點調一點,那大概是永遠調不出來了。。。

 

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;
}