noip模擬賽 水管工的難題
【問題描述】
你是一名優秀的水管工。 一天你遇到了一個棘手的難題。 你需要在一個長方體狀的房間
內連接一條貫穿房間內部的水管。房間的長為 X,寬為 Y,高為 Z, 整個房間可以看成是 X×Y×Z
個小立方體空間組成的。 如果位房間建立直角坐標系,則房間內每個小立方體空間都可以用
一個三維坐標(x,y,z)表示, 房間一角的小立方體的坐標為(1,1,1), 它的對角的坐標為(X,Y,Z),
其余小立方體的坐標的三個維度都分別落在 1…X, 1…Y 和 1…Z 內。 水管的入口和出口已經
開鑿好了,它們位於長方體的表面(可能位於房間的地板、墻壁或天花板上),且正好占據
了某個小立方體的一個面。下圖展示了房間的立方體空間的劃分方式和一種開鑿水管出入口
的方式。
你的手頭只有一種形狀的水管部件, 它呈 L 型,且正好占據了 4 個小立方體空間, 如下
圖所示, 灰色部分是水管部件兩端的開口位置。
你可以任意旋轉、翻轉水管部件,然後將它的開口接到入口、 出口或者其它水管部件的
開口上。 水管的開口必須正好接在入口或出口上才算接上,伸出房間外一截是不行的。 下圖
展示了一種兩個水管部件的連接方式。
在連接水管時, 水管部件間不能相交,也不能伸出房間之外。房間內有一些小立方體空
間已經被物品占據了,水管也不能經過那些格子。 另外,水管部件數量有限,你的手頭只有
12 個這樣的零件。
現在,給出房間的長、高、 寬, 以及入口、出口的位置和房間內物品的坐標,請你計算 至少需要多少個這樣的水管部件才能完成任務,或者判斷無法完成任務。
【輸入】
輸入的第 1 行包含 4 個正整數 X, Y, Z, m,其中 X,Y,Z 表示房間的長、高、寬, m 表示
房間中物品的數量。
輸入的第 2 行包含 3 個正整數 x1, y1, z1, 和一個字符 ch。 其中(x1,y1,z1)是水管入口所
在的小立方體的坐標。 ch 的值為‘x‘、 ‘y‘或‘z‘, 用於指示入口所在的面。 當 ch 為‘x‘時,表示
入口所在的面與 YZ 坐標平面平行, 當 ch 為‘y‘時,表示入口所在的面與 XZ 坐標平面平行,
當 ch 為‘z‘時,表示入口所在的面與 XY 坐標平面平行。 輸入保證入口所在的面在長方體表面
上。 數字和數字、數字和字符間用一個空格隔開。
輸入的第 3 行包含 3 個正整數 x2, y2, z2,和一個字符 ch, 表示水管出口的位置, 意義
與格式同上。
接下來 m 行,每行包含三個正整數 xi, yi, zi,表示在坐標(xi,yi,zi)的小立方體空間內有
物品。
【輸出】
輸出包含 1 個整數,表示最少使用的水管部件的數量。 如果不能完成接水管的任務,輸
出“impossible”, 不含引號。
【輸入輸出樣例 1】
plumber.in | plumber.out |
5 4 3 1 3 1 1 z 1 4 3 x 2 3 3 |
2 |
見選手目錄下的 plumber/plumber1.in 與 plumber/plumber1.out
【輸入輸出樣例 1 說明】
入口和出口的位置如圖所示:
分析:就是一道爆搜題.dfs,記錄放的水管的最後一個位置和方向,除了這個方向和它對面的方向以外都可以放,然後就有兩種方式了,討論一下,枚舉一下旋轉方向就行了.
直接搜會T掉,要加上剪枝.可行性剪枝似乎不行,考慮最優性剪枝,如果剪枝僅僅是如果當前用的水管數>ans就返回,還是會T掉4個點,需要用上一個估價函數.考慮3維空間上的曼哈頓距離,每用一個水管當前位置與終點位置的曼哈頓距離就會-4,計算一下當前點與終點的曼哈頓距離為多少就能計算出至少還需要用多少根水管,就能最優性剪枝了.
#include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int dx[] = { 1, -1, 0, 0, 0, 0 }, dy[] = { 0, 0, 1, -1, 0, 0 }, dz[] = { 0, 0, 0, 0, 1, -1 }; int X, Y, Z, m, ans = 13, tx, ty, tz, tdir, sx, sy, sz, sdir; bool vis[30][30][30], a[30][30][30]; bool check(int x, int y, int z) { if (x >= 1 && x <= X && y >= 1 && y <= Y && z >= 1 && z <= Z && !vis[x][y][z] && !a[x][y][z]) return true; return false; } void dfs(int x, int y, int z, int dir, int p) { int dis = abs(tx - x) + abs(ty - y) + abs(tz - z); if (p + dis / 4>= ans) return; if (x == tx && y == ty && z == tz && dir == tdir) { ans = min(ans, p); return; } if (check(x + dx[dir], y + dy[dir], z + dz[dir]) && check(x + 2 * dx[dir], y + 2 * dy[dir], z + 2 * dz[dir])) { vis[x + dx[dir]][y + dy[dir]][z + dz[dir]] = 1; vis[x + 2 * dx[dir]][y + 2 * dy[dir]][z + 2 * dz[dir]] = 1; int nx = x + 2 * dx[dir], ny = y + 2 * dy[dir], nz = z + 2 * dz[dir]; for (int i = 0; i < 6; i++) { if ((i / 2) != (dir / 2)) { if (check(nx + dx[i], ny + dy[i], nz + dz[i]) && check(nx + 2 * dx[i], ny + 2 * dy[i], nz + 2 * dz[i])) { vis[nx + dx[i]][ny + dy[i]][nz + dz[i]] = vis[nx + 2 * dx[i]][ny + 2 * dy[i]][nz + 2 * dz[i]] = 1; dfs(nx + 2 * dx[i], ny + 2 * dy[i], nz + 2 * dz[i], i, p + 1); vis[nx + dx[i]][ny + dy[i]][nz + dz[i]] = vis[nx + 2 * dx[i]][ny + 2 * dy[i]][nz + 2 * dz[i]] = 0; } } } if (check(x + 3 * dx[dir], y + 3 * dy[dir], z + 3 * dz[dir])) { vis[x + 3 * dx[dir]][y + 3 * dy[dir]][z + 3 * dz[dir]] = 1; int nx = x + 3 * dx[dir], ny = y + 3 * dy[dir], nz = z + 3 * dz[dir]; for (int i = 0; i < 6; i++) { if ((i / 2) != (dir / 2)) { if (check(nx + dx[i], ny + dy[i], nz + dz[i])) { vis[nx + dx[i]][ny + dy[i]][nz + dz[i]] = 1; dfs(nx + dx[i], ny + dy[i], nz + dz[i], i, p + 1); vis[nx + dx[i]][ny + dy[i]][nz + dz[i]] = 0; } } } vis[x + 3 * dx[dir]][y + 3 * dy[dir]][z + 3 * dz[dir]] = 0; } vis[x + dx[dir]][y + dy[dir]][z + dz[dir]] = 0; vis[x + 2 * dx[dir]][y + 2 * dy[dir]][z + 2 * dz[dir]] = 0; } } int main() { scanf("%d%d%d%d", &X, &Y, &Z, &m); char s[2]; scanf("%d%d%d", &sx, &sy, &sz); scanf("%s", s); sdir = (s[0] - ‘x‘) * 2; //找起點 if (s[0] == ‘x‘) sdir += (sx == X); else if (s[0] == ‘y‘) sdir += (sy == Y); else if (s[0] == ‘z‘) sdir += (sz == Z); sx -= dx[sdir]; sy -= dy[sdir]; sz -= dz[sdir]; scanf("%d%d%d", &tx, &ty, &tz); scanf("%s", s); tdir = (s[0] - ‘x‘) * 2; if (s[0] == ‘x‘) tdir += (tx == 1); else if (s[0] == ‘y‘) tdir += (ty == 1); else if (s[0] == ‘z‘) tdir += (tz == 1); for (int i = 1; i <= m; i++) { int x, y, z; scanf("%d%d%d", &x, &y, &z); a[x][y][z] = 1; } dfs(sx, sy, sz, sdir, 0); if (ans == 13) printf("impossible\n"); else printf("%d\n", ans); return 0; }
noip模擬賽 水管工的難題