「程序設計思維與實踐」Week2 作業:A-Maze、B-Pour Water。BFS+路徑記錄輸出+map+set+pair

A - Maze

題目描述

東東有一張地圖,想通過地圖找到妹紙。地圖顯示,0表示可以走,1表示不可以走,左上角是入口,右下角是妹紙,這兩個位置保證爲0。既然已經知道了地圖,那麼東東找到妹紙就不難了,請你編一個程序,寫出東東找到妹紙的最短路線。

input

輸入是一個5 × 5的二維數組,僅由0、1兩數字組成,表示法陣地圖。

Output

輸出若干行,表示從左上角到右下角的最短路徑依次經過的座標,格式如樣例所示。數據保證有唯一解。

Sample Input

0 1 0 0 0
0 1 0 1 0
0 1 0 1 0
0 0 0 1 0
0 1 0 1 0

Sample Output

(0, 0)
(1, 0)
(2, 0)
(3, 0)
(3, 1)
(3, 2)
(2, 2)
(1, 2)
(0, 2)
(0, 3)
(0, 4)
(1, 4)
(2, 4)
(3, 4)
(4, 4)

Hint

座標(x, y)表示第x行第y列,行、列的編號從0開始,且以左上角爲原點。
另外注意,輸出中分隔座標的逗號後面應當有一個空格。

題解

  • 題意找到最短路線,所有路徑的長度都是1,用bfs遍歷第一次找到終點時即爲最短路徑。
  • 路徑記錄:創建一個pair<int,int>類型的數組,在將節點推入隊列時,記錄從哪個節點走到了當前節點。遍歷完成後從終點逆序推到起點,按正向輸出路徑即可。

代碼

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <stack>
using namespace std;

int maps[10][10];
pair<int, int> pre[10][10];
bool vis[10][10];

void mapsInput(){
	for(int i = 0; i <= 4; ++ i) {
		for(int j = 0; j <= 4; ++ j) {
			cin >> maps[i][j];
		}
	}

}

int dx[] = {0,0,-1,0,1};
int dy[] = {0,-1,0,1,0};

queue<pair<int, int> > q;
void bfs(int s, int t) {
	q.push({s,t});
	vis[s][t] = true;
	while(q.size()) {
		pair<int, int> now = q.front();
		q.pop();
		if(now.first == 4 && now.second == 4)
			break;
		for(int i = 1; i <= 4; ++i) {
			int nx = now.first + dx[i];
			int ny = now.second + dy[i];
			if(!vis[nx][ny] && nx>=0 && nx<=4 && ny>=0 && ny<=4 && !maps[nx][ny]) {
				q.push({nx,ny});
				// cout << " success";
				pre[nx][ny] = {now.first, now.second};
				vis[nx][ny] = true;
			} else {
				// cout << "~~~" << vis[nx][ny] << " " << maps[nx][ny];
			}
		}
	}
}

int main() {
	memset(pre,0,sizeof(pre));
	memset(vis,0,sizeof(vis));
	while(q.size()) q.pop();
	mapsInput();

	bfs(0,0);
	pair<int, int> t(4,4);

	stack<pair<int, int> > s;
	while(t.first != 0 ||  t.second != 0) {
		s.push(t);
		t = pre[t.first][t.second];
	}
	s.push({0,0});
	while(s.size()) {
		cout << "(" << s.top().first << ", " << s.top().second << ")\n";
		s.pop();
	}

	return 0;
}

B - Pour Water

題目描述

倒水問題 “fill A” 表示倒滿A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯並且把B杯倒滿或A倒空。

input

輸入包含多組數據。每組數據輸入 A, B, C 數據範圍 0 < A <= B 、C <= B <=1000 、A和B互質。

Output

你的程序的輸出將由一系列的指令組成。這些輸出行將導致任何一個罐子正好包含C單位的水。每組數據的最後一行輸出應該是“success”。輸出行從第1列開始,不應該有空行或任何尾隨空格。

Sample Input

2 7 5
2 7 4

Sample Output

fill B
pour B A
success 
fill A
pour A B
fill A
pour A B
success

Hint

如果你的輸出與Sample Output不同,那沒關係。對於某個"A B C"本題的答案是多解的,不能通過標準的文本對比來判定你程序的正確與否。 所以本題由 SPJ(Special Judge)程序來判定你寫的代碼是否正確。

題解

  • 進行bfs暴力枚舉每種狀態:從每個狀態有6中方向可以轉移,分別是倒滿A、倒滿B、倒空A、倒空B、把A往B倒、把B往A倒。
  • 使用set作爲傳統的vis數組,來判斷某種狀態是否已經到達過,注意每組數據開始時要清空。
  • 使用prestatus和preid兩個map作爲路徑記錄和操作記錄,即記錄到達某個狀態的操作類型(1/6)和前一個狀態,bfs返回最終狀態,從最終狀態倒推回去即可輸出路徑。
  • 將6種操作用函數數組來整合能夠複用check操作的代碼,稍微美觀清晰一些。

代碼

醜陋版
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <stack>
#include <map>
#include <set>
using namespace std;

map<int,string> method;

int A, B, C;
struct status {
	int a;
	int b;
	bool operator < (const status &x) const {
		if(a != x.a) return a < x.a;
		return b < x.b;
	}
};

map<status,status> prestatus;
map<status,int> preid;
set<status> vis;
queue<status> q;
void check(status now, status t, int id) {
	if(vis.find(t) == vis.end()) { //沒有走過這個狀態
		// cout << "*";
		q.push(t);
		vis.insert(t);
		preid[t] = id;
		prestatus[t] = now;
	}
	// cout << endl;
}

status bfs(int a, int b) {
	q.push((status){a,b});
	vis.insert((status){a,b});
	while(q.size()) {
		status now = q.front();
		q.pop();
		if(now.a == C || now.b == C) {
			// cout << now.a << " " << now.b;
			return now;
		}
		// 6種分支生成狀態
		status t;
		// 1 fill A
		t.a=now.a;t.b=now.b;
		t.a = A;
		check(now,t,1);
		// 2 fill B
		t.a=now.a;t.b=now.b;
		t.b = B;
		check(now,t,2);
		// 3 empty A
		t.a=now.a;t.b=now.b;
		t.a = 0;
		check(now,t,3);
		// 4 empty B
		t.a=now.a;t.b=now.b;
		t.b = 0;
		check(now,t,4);
		// 5 pour A B
		t.a=now.a;t.b=now.b;
		int minn = min(t.a, B-t.b);
		t.a -= minn;
		t.b += minn;
		check(now,t,5);
		// 6 pour B A
		t.a=now.a;t.b=now.b;
		minn = min(t.b, A-t.a);
		t.b -= minn;
		t.a += minn;
		check(now,t,6);
	}
	return (status){0,0};
}

void init() {
	method[1] = "fill A";
	method[2] = "fill B";
	method[3] = "empty A";
	method[4] = "empty B";
	method[5] = "pour A B";
	method[6] = "pour B A";
}

stack<string> haha;

int main() {
	init();
	while(cin >> A >> B >> C) {
		prestatus.clear();
		preid.clear();
		vis.clear();;
		while(q.size() > 0) {q.pop();}
		status ans = bfs(0,0);	
		while(ans.a != 0 || ans.b != 0) {
			// cout << method[preid[ans]] << endl;
			haha.push(method[preid[ans]]);
			ans = prestatus[ans];
		}
		while(haha.size()) {
			cout << haha.top() << endl;
			haha.pop();
		}
		puts("success");
	}
		
	return 0;
}
重構版加入函數指針數組
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <stack>
#include <map>
#include <set>
using namespace std;

map<int,string> method;

int A, B, C;
struct status {
	int a;
	int b;
	bool operator < (const status &x) const {
		if(a != x.a) return a < x.a;
		return b < x.b;
	}
	status& operator = (const status &x) {
		a = x.a;
		b = x.b;
		return *this;
	}
};

map<status,status> prestatus;
map<status,int> preid;
set<status> vis;
queue<status> q;
void check(status now, status t, int id) {
	if(vis.find(t) == vis.end()) { //沒有走過這個狀態
		// cout << "*";
		q.push(t);
		vis.insert(t);
		preid[t] = id;
		prestatus[t] = now;
	}
	// cout << endl;
}

// 1 fill A
void one(status now, status &t, int id) {
	t.a = A;
}
// 2 fill B
void two(status now, status &t, int id) {
	t.b = B;
}
// 3 empty A
void three(status now, status &t, int id) {
	t.a = 0;
}
// 4 empty B
void four(status now, status &t, int id) {
	t.b = 0;
	check(now,t,4);
}
// 5 pour A B
void five(status now, status &t, int id) {
	t.a=now.a;t.b=now.b;
	int minn = min(t.a, B-t.b);
	t.a -= minn;
	t.b += minn;
	check(now,t,5);
}
// 6 pour B A
void six(status now, status &t, int id) {
	t.a=now.a;t.b=now.b;
	int minn = min(t.b, A-t.a);
	t.b -= minn;
	t.a += minn;
}

void (*p[6])(status now, status &t, int id);
status bfs(int a, int b) {
	q.push((status){a,b});
	vis.insert((status){a,b});
	while(q.size()) {
		status now = q.front();
		q.pop();
		if(now.a == C || now.b == C) {
			return now;
		}
		// 6種分支生成狀態
		for(int i = 1; i <= 6; ++ i) {
			status t = now;
			p[i](now,t,i);
			check(now,t,i);
		}
	}
	return (status){0,0};
}

void init() {
	method[1] = "fill A";
	method[2] = "fill B";
	method[3] = "empty A";
	method[4] = "empty B";
	method[5] = "pour A B";
	method[6] = "pour B A";
	p[1] = one; p[2] = two; p[3] = three; 
	p[4] = four; p[5] = five; p[6] = six;
}

stack<string> haha;
int main() {
	init();
	while(cin >> A >> B >> C) {
		prestatus.clear();
		preid.clear();
		vis.clear();;
		while(q.size() > 0) {q.pop();}
		status ans = bfs(0,0);	
		while(ans.a != 0 || ans.b != 0) {
			// cout << method[preid[ans]] << endl;
			haha.push(method[preid[ans]]);
			ans = prestatus[ans];
		}
		while(haha.size()) {
			cout << haha.top() << endl;
			haha.pop();
		}
		puts("success");
	}
		
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章