1. 程式人生 > 實用技巧 >洛谷 P1312 Mayan遊戲

洛谷 P1312 Mayan遊戲

題目傳送門

大模擬,用的bfs,完全按照題目描述做即可,其實本題用dfs要更好寫.

我的做法有點缺陷,但是不知道在哪裡,不開\(O_2\)優化會T一個點.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>

using namespace std;

int n,a[10][10],oi,f_step;
string f_ans;//最後答案 
map<string,int> p;//到當前狀態的步數 
map<string,bool> vis;//bfs用 
map<string,string> ans;//到當前的狀態的過程 
queue<string> w;

inline bool check(string l) {//檢查是否到了全部消掉的狀態 
	for(int i = 0;i < l.length(); i++)
		if(l[i] != '0') return 0;
	return 1;
}

inline bool _compare(string l) {//更新答案 
	string u = ans[l];
	for(int i = 0;i < min(u.length(),f_ans.length()); i++) {
		int s1,s2;
		s1 = u[i] - '0';
		s2 = f_ans[i] - '0';
		if((i + 1) % 3 == 0) {
			if(s1 > s2) return 1;
			if(s1 < s2) return 0;
		}
		else {
			if(s1 < s2) return 1;
			if(s1 > s2) return 0;
		}
	}
	return 1;
}

inline void update_answer(string l) {
	if(_compare(l)) {
		f_step = p[ans[l]];
		f_ans = ans[l];
	} 
}

inline string chuli(string l) {//移動方塊後,處理能消除的塊及讓懸空的塊落下去 
	int s[10][10],len = -1;
	bool flag[10][10],oo = 0;
	memset(flag,0,sizeof(flag));
	for(int i = 0;i <= 6; i++) {
		for(int j = 0;j <= 4; j++) {
			s[i][j] = l[++len] - '0';
		}
	}
	while(true) {
		memset(flag,0,sizeof(flag));
		oo = 0;
		for(int i = 0;i <= 6; i++) {
			for(int j = 0;j <= 4; j++){
				if(s[i][j] == 0)
					continue;
				else {
					int pp = i;
					while(true) {
						if(s[pp-1][j] != 0) break;
						if(pp == 0) break;
						s[pp-1][j] = s[pp][j];
						s[pp][j] = 0;
						pp--;
					}
				}
			}
		}
		for(int i = 0;i <= 6; i++) {
			for(int j = 0;j <= 4; j++) {
				if(s[i][j] == 0) continue;
				if(s[i][j] == s[i][j+1] && s[i][j] == s[i][j+2] && s[i][j] != 0)
					flag[i][j] = flag[i][1+j] = flag[i][j+2] = 1,oo = 1;
				if(s[i][j] == s[i+1][j] && s[i][j] == s[i+2][j] && s[i][j] != 0)
					flag[i][j] = flag[i+1][j] = flag[i+2][j] = 1,oo = 1;
			}
		}
		for(int i = 0;i <= 6; i++) {
			for(int j = 0;j <= 4; j++)
				if(flag[i][j])
					s[i][j] = 0;
		}
		if(oo == 0) break;	
	}
	len = -1;
	for(int i = 0;i <= 6; i++)
		for(int j = 0;j <= 4; j++)
			l[++len] = char(s[i][j] + 48);
	return l;
}

inline string bj(string x1,string x2) {//只有在當前走的答案比已知的答案最優才會更新(最優性剪枝) 
	for(int i = 0;i < min(x1.length(),x2.length()); i++) {
		int s1,s2;
		s1 = x1[i] - '0';
		s2 = x2[i] - '0';
		if((i + 1) % 3 == 0) {
			if(s1 > s2) return x1;
			if(s1 < s2) return x2;
		}
		else {
			if(s1 < s2) return x1;
			if(s1 > s2) return x2;
		}
	}
	return x2;
}

inline void bfs() {
	while(!w.empty()) {
		string u = w.front();
		w.pop();
		vis[u] = 0;
		if(p[u] >= n + 1) continue;
		for(int i = 0;i < 35; i++) {
			int x = i / 5;
			int y = i % 5;
			int step = p[u];
			if(u[i] == '0') continue;
			for(int k = 1;k >= -1; k -= 2) {
				string l = u;
				if(y == 4 && k == 1) continue;
				if(y == 0 && k == -1) continue;
				if(k == 1 && l[i] == l[i+1]) continue;
				if(k == -1 && l[i-1] != '0') continue;
				char o = l[i];
				l[i] = l[i+k];
				l[i+k] = o;
				l = chuli(l);
				string pp = ans[u];
				pp = pp + char(y+48) + char(x+48) + char(k+48);
				if(p[l] >= step + 1 || p[l] == 0) {//只有在步數更優的情況下才會更新ans 
					if(ans.count(l)) ans[l] = bj(pp,ans[l]);
					else ans[l] = pp;
				}
				string aa = ans[l];
				if(check(l)) {
					oi = 1;
					update_answer(l);
				}
				if(!vis[l] && _compare(l)) {
					vis[l] = 1;
					p[l] = p[u] + 1;
					w.push(l);
				}
			}
		}
	}
}

int main() {
	scanf("%d",&n);
	string l;
	for(int i = 0;i <= 4; i++)
		for(int j = 0;j <= 20; j++) {
			scanf("%d",&a[j][i]);
			if(a[j][i] == 0) break;
		}
	for(int i = 0;i <= 6; i++) {
		for(int j = 0;j <= 4; j++) {
			char u = char(a[i][j] + 48);
			l = l + u;
		}
	}
	f_ans = "g";
	p[l] = 1;
	vis[l] = 1;
	w.push(l);
	bfs();
	if(oi == 0) printf("-1");
	else {
		for(int i = 0;i < f_ans.length(); i++) {
			if(f_ans[i] == '/')
				cout << "-1";
			else cout << f_ans[i] << ' ';
			if(i % 3 == 2) cout << endl;
		}
	}
	return 0;
}