[codevs2495]水叮当的舞步
试题描述
水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变。
为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~
地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色。
水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。
由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把所有格子的颜色变成一样的。
输入
每个测试点包含多组数据。
每组数据的第一行是一个整数N,表示地摊上的格子有N行N列。
接下来一个N*N的矩阵,矩阵中的每个数都在0~5之间,描述了每个格子的颜色。
N=0代表输入的结束。
输出
对于每组数据,输出一个整数,表示最少步数。
输入示例
输出示例
数据规模及约定
对于30%的数据,N<=5
对于50%的数据,N<=6
对于70%的数据,N<=7
对于100%的数据,N<=8,每个测试点不多于20组数据。
题解
依然是迭代加深搜索。
但是需要几个优化。
我先贴一份 T 的代码上来:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std; int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
} #define maxn 10
int n, A[maxn][maxn]; struct Point {
int x, y;
Point() {}
Point(int _, int __): x(_), y(__) {}
} Q[maxn*maxn];
int hd, tl, dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0};
bool vis[maxn][maxn], get[maxn];
bool isin(int x, int y) { return 1 <= x && x <= n && 1 <= y && y <= n; }
void bfs() {
memset(vis, 0, sizeof(vis)); vis[1][1] = 1;
hd = tl = 0; Q[++tl] = Point(1, 1);
memset(get, 0, sizeof(get));
while(hd < tl) {
Point u = Q[++hd];
for(int i = 0; i < 4; i++) {
Point v(u.x + dx[i], u.y + dy[i]);
if(isin(v.x, v.y) && !vis[v.x][v.y]) {
if(A[v.x][v.y] != A[1][1]) get[A[v.x][v.y]] = 1;
else vis[v.x][v.y] = 1, Q[++tl] = v;
}
}
}
return ;
}
void print() {
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) printf("%d%c", A[i][j], j < n ? ' ' : '\n');
putchar('\n');
return ;
}
bool dfs(int cur, int K) {
bool ok = 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) if(A[i][j] != A[1][1]) {
ok = 0; break;
}
if(!ok) break;
}
// printf("%d(%d):\n", cur, K); print();
if(ok) return printf("%d\n", K), 1;
if(cur == K) return 0;
// printf("%d(%d):\n", cur, K); print();
int B[maxn][maxn];
bool Vis[maxn][maxn], Get[maxn];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) B[i][j] = A[i][j];
bfs();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) Vis[i][j] = vis[i][j];
for(int i = 0; i <= 5; i++) Get[i] = get[i];
for(int i = 0; i <= 5; i++) if(Get[i]) {
for(int x = 1; x <= n; x++)
for(int y = 1; y <= n; y++) if(Vis[x][y])
A[x][y] = i;
// printf("%d(%d) turn to:\n", cur, K); print();
if(dfs(cur + 1, K)) return 1;
for(int x = 1; x <= n; x++)
for(int y = 1; y <= n; y++) A[x][y] = B[x][y];
}
return 0;
} int main() {
while(1) {
n = read(); if(!n) break;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) A[i][j] = read(); for(int i = 0; !dfs(0, i); i++) ;
} return 0;
}
这份代码中 dfs 每深入一层,就必定进行一遍 bfs,是非常耗时间的。
所以我们考虑不用 bfs,模了一下黄学长的题解后,明白了可以通过打标记的方式维护当前状态,即:已经同色的区域用 1 做标记,同色区域周围用 2 做标记,然后每次向外扩展时显然只用考虑标 2 的部分,我们动态地更新这些标记,就不用每次都 bfs 了。
但这样还不够,我们需要加剪枝:最好的情况莫过于每次减少当前地图中的一种颜色,如果“当前地图没标 1 的部分的颜色种类数 + 当前步数 > 迭代加深搜索限制的步数”的话,就可以跳出了。
此外,向外扩张时,已经考虑过的颜色不必重复考虑了,这也是一个重要的优化。
最终 AC 代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std; int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
} #define maxn 10
int n, A[maxn][maxn], vis[maxn][maxn], dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0}; void print() {
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) printf("%d%c", vis[i][j], j < n ? ' ' : '\n');
putchar('\n');
return ;
}
bool isin(int x, int y) { return 1 <= x && x <= n && 1 <= y && y <= n; } void dfs(int x, int y, int col) {
vis[x][y] = 1;
for(int i = 0; i < 4; i++) {
int vx = x + dx[i], vy = y + dy[i];
if(isin(vx, vy) && !vis[vx][vy]) {
vis[vx][vy] = 2;
if(A[vx][vy] == col) dfs(vx, vy, col);
}
}
return ;
}
void mark(int col) {
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(vis[i][j] == 2 && A[i][j] == col) dfs(i, j, col);
return ;
}
int cal() {
bool has[6]; memset(has, 0, sizeof(has));
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(vis[i][j] != 1) has[A[i][j]] = 1;
int cnt = 0;
for(int i = 0; i < 6; i++) cnt += has[i];
return cnt;
}
bool solve(int cur, int K) {
// printf("%d(%d):\n", cur, K); print();
int cnt = cal();
if(!cnt) return printf("%d\n", K), 1;
if(cur == K || cnt + cur > K) return 0;
int B[maxn][maxn];
bool has[6]; memset(has, 0, sizeof(has));
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) if(vis[i][j] == 2 && !has[A[i][j]]) {
memcpy(B, vis, sizeof(vis));
mark(A[i][j]); has[A[i][j]] = 1;
if(solve(cur + 1, K)) return 1;
memcpy(vis, B, sizeof(vis));
}
return 0;
} int main() {
while(1) {
n = read(); if(!n) break;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) A[i][j] = read(); memset(vis, 0, sizeof(vis));
vis[1][1] = 2; mark(A[1][1]);
for(int i = 0; !solve(0, i); i++) ;
} return 0;
}