[codevs2495]水叮當的舞步


[codevs2495]水叮當的舞步

試題描述

  水叮當得到了一塊五顏六色的格子形地毯作為生日禮物,更加特別的是,地毯上格子的顏色還能隨着踩踏而改變。
  為了討好她的偶像虹貓,水叮當決定在地毯上跳一支輕盈的舞來賣萌~~~

  地毯上的格子有N行N列,每個格子用一個0~5之間的數字代表它的顏色。
  水叮當可以隨意選擇一個0~5之間的顏色,然后輕輕地跳動一步,左上角的格子所在的聯通塊里的所有格子就會變成她選擇的那種顏色。這里連通定義為:兩個格子有公共邊,並且顏色相同。
  由於水叮當是施展輕功來跳舞的,為了不消耗過多的真氣,她想知道最少要多少步才能把所有格子的顏色變成一樣的。

輸入

每個測試點包含多組數據。
每組數據的第一行是一個整數N,表示地攤上的格子有N行N列。
接下來一個N*N的矩陣,矩陣中的每個數都在0~5之間,描述了每個格子的顏色。
N=0代表輸入的結束。

輸出

對於每組數據,輸出一個整數,表示最少步數。

輸入示例

2
0 0
0 0
3
0 1 2
1 1 2
2 2 1
0

輸出示例

0
3

數據規模及約定

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

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com