[kuangbin带你飞] 专题二

1、HDU 1043 Eight

题意:

给定一个初始网格,通过最少移动次数,得到正确排列。
如果没有解决方案,输出unsolvable。
如果有解决方案,输出移动方法
有多组输入

题解:

反向bfs,直接将所有方案给跑出来,边跑边记录方向。

代码:

#include <iostream> 
#include <set> 
#include <map>
#include <queue>
#include <string>
#include <algorithm>
#include <sstream>

using namespace std;

typedef unsigned int uInt;

const int N = 181450;
const uInt goal = 6053444;
const int dr[] = {0, 0, 1, -1};
const int dc[] = {1, -1, 0, 0};

int p[9], a[3][3], tot = 0;
char ans[100];
string str;
map<uInt, int> info;

void intToArray (uInt num, int &x, int &y) {
	for (int i = 2; i >= 0; i--) {
		for (int j = 2; j >= 0; j--) {
			a[i][j] = num % 9;
			num /= 9;
			if (a[i][j] == 8) {
				x = i;
				y = j;
			}
		}
	}
}

uInt arrayToInt () {
	int num = 0;
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			num = num * 9 + a[i][j];
		}
	}
	return num;
}

void bfs () {
	queue<int> q;
	q.push(goal);
	info[goal] = 0;
	uInt node1, node2;
	int x, y, nx, ny, g, h;
	while (!q.empty()) {
		node1 = q.front(); q.pop();
		if (node1 == 6053596) {
			int z = 0;
		}
		intToArray(node1, x, y);
		for (int i = 0; i < 4; i++) {
			nx = x + dr[i]; ny = y + dc[i];
			if (nx >= 0 && ny >= 0 && nx < 3 && ny < 3) {
				swap(a[x][y], a[nx][ny]);
				node2 = arrayToInt();
				if (!info.count(node2)) {
					q.push(node2);
					info[node2] = i;
				}
				swap(a[x][y], a[nx][ny]);
			}
		}
	}
}

int main () {
	bfs();
	while (getline(cin, str)) {
		stringstream scin(str);
		char c;
		int x, y;
		uInt node;
		for (int i = 0; i < 9; i++) {
			scin >> c;
			if (c != 'x') p[i] = c - '0' - 1;
			else p[i] = 8, x= i / 3, y = i % 3;
			a[i/3][i%3] = p[i];
		}
		node = arrayToInt();
		if (info.count(node)) {
			for (tot = 0; node != goal; tot++) {
				int dir = info[node];
				switch (dir) {
					case 0: ans[tot] = 'l'; break;
					case 1: ans[tot] = 'r'; break;
					case 2: ans[tot] = 'u'; break;
					case 3: ans[tot] = 'd'; break;
				}
				swap(a[x][y], a[x-dr[dir]][y-dc[dir]]);
				x -= dr[dir];
				y -= dc[dir];
				node = arrayToInt();
			}
			for (int i = 0; i < tot; i++) {
				printf("%c", ans[i]);
			}
			printf("\n");
		} else {
			printf("unsolvable\n");
		}
	}
	return 0;
}

2、HDU 3567 Eight II

题意:

八数码问题
给定状态A和状态B,输出从状态A到状态B最少需要几步,以及操作。要求操作的字典序最小。

题解:

双向bfs,同时需要注意字典序最小

代码:

#include <cstdio>
#include <map>
#include <queue>

using namespace std;

typedef unsigned int uInt;

const int dr[2][4] = {{1, 0, 0, -1}, {-1, 0, 0, 1}};
const int dc[2][4] = {{0, -1, 1, 0}, {0, 1, -1, 0}};
const char op[] = {'d', 'l', 'r', 'u'};

int a[2][3][3], tot, val1, val2;
char path[50];
map<uInt, int> info[2];
queue<int> q[2];

void clear () {
	info[0].clear(); 
	info[1].clear();
	while (!q[0].empty()) q[0].pop();
	while (!q[1].empty()) q[1].pop();
}

void input (int x) {
	char c;
	getchar();
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			c = getchar();
			if (c == 'X') a[x][i][j] = 0;
			else a[x][i][j] = c - '0';
		}
	}
}

void intToArray (uInt num, int index, int &x, int &y) {
	for (int i = 2; i >= 0; i--) {
		for (int j = 2; j >= 0; j--) {
			a[index][i][j] = num % 9;
			num /= 9;
			if (a[index][i][j] == 0) {
				x = i;
				y = j;
			}
		}
	}
}

uInt arrayToInt (int index) {
	int num = 0;
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			num = num * 9 + a[index][i][j];
		}
	}
	return num;
}

void findPath (int index, int start, int goal, char path[50], int &tot) {
	int x, y;
	intToArray(start, index, x, y);
	int node = start;
	for (tot = 0; node != goal; tot++) {
		int dir = info[index][node] % 10;
		path[tot] = op[dir];
		swap(a[index][x][y], a[index][x-dr[index][dir]][y-dc[index][dir]]);
		x -= dr[index][dir];
		y -= dc[index][dir];
		node = arrayToInt(index);
	}
}

uInt bfs(uInt node1, uInt node2) {
	int dis[2] = {10000, 10000};
	q[0].push(node1);
	q[1].push(node2);
	info[0][node1] = 0;
	info[1][node2] = 0;
	uInt ans = 0;
	int d, nx, ny, x, y;
	for (int i = 1; ; i = (i + 1) % 2) {
		for (int k = q[i].size() - 1; k >= 0; k--)  {
			node1 = q[i].front(); q[i].pop();
			d = info[i][node1] / 10;
			intToArray(node1, i, x, y);
			for (int j = 0; j < 4; j++) {
				nx = x + dr[i][j]; ny = y + dc[i][j];
				if (nx >= 0 && ny >= 0 && nx < 3 && ny < 3) {
					swap(a[i][x][y], a[i][nx][ny]);
					node2 = arrayToInt(i);
					if (!info[i].count(node2)) {
						q[i].push(node2);
						info[i][node2] = (d + 1) * 10 + j;
					} else if (i == 1) {
						int val = info[i][node2];
						if (val / 10 >= d + 1 && val % 10 >= j) {
							// 确保从状态mid到状态B的路径的字典序最小 
							info[i][node2] = (d + 1) * 10 + j;
						}
					}
					swap(a[i][x][y], a[i][nx][ny]);
					// 只有i == 0时才能确保从状态A到状态mid的字典序最小 
					if (i == 0 && info[0].count(node2) && info[1].count(node2)) return node2;
				}
			}
		}
	}
}

int main () {
//	freopen("in.txt", "r", stdin);
	int T;
	scanf("%d", &T);
	for (int kase = 1; kase <= T; kase++) {
		clear();
		char c;
		input(0);
		input(1);
		val1 = arrayToInt(0);
		val2 = arrayToInt(1);
		if (val1 == val2) {
			printf("Case %d: 0\n\n", kase);
			continue;
		}
		uInt mid = bfs(val1, val2);
		printf("Case %d: %d\n", kase, info[0][mid] / 10 + info[1][mid] / 10);
		findPath(0, mid, val1, path, tot);
		for (int i = tot - 1; i >= 0; i--) {
			printf("%c", path[i]);
		}
		findPath(1, mid, val2, path, tot);
		for (int i = 0; i < tot; i++) {
			printf("%c", path[i]);
		}
		printf("\n");
	}
	return 0;
}
3、HDU 2181 哈密顿绕行世界问题

题意:
给定20个点,每个点有三条边。按照字典序输出从一个点出发经过每个点正好一次的路线。

题解:
非常简单,dfs就可以了,输入边的时候记得排序,确保路线的字典序从小到达

代码:

#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

const int N = 21;

vector<int> edge[N];
int d[N], cnt = 0, m;
bool vis[N], ok[N];

void dfs (int cur, int x) {
	if (cur == 20)  {
		// 判断最后是否可以回到m 
		bool flag = false;
		for (int i = 0; i < 3; i++) {
			if (edge[x][i] == m) flag = true;
		}
		if (!flag) return ;
		printf("%d: ", ++cnt);
		for (int i = 0; i <= 20; i++) {
			printf(" %d", d[i]);
		}
		printf("\n");
	} else {
		for (int i = 0; i < 3; i++) {
			int y = edge[x][i];
			if (vis[y]) continue;
			vis[y] = true;
			d[cur] = y;
			dfs(cur + 1, y);
			vis[y] = false;
		}
	}
}

int main () {
	for (int x = 1, y; x <= 20; x++) {
		edge[x].clear();
		for (int i = 0; i < 3; i++) {
			scanf("%d", &y);
			if (y == 0) return 0;
			edge[x].push_back(y);
		}
		// 排序,确定字典序从小到大 
		sort(edge[x].begin(), edge[x].end());
	}
	while (scanf("%d", &m) && m != 0) {
		cnt = 0;
		d[0] = d[20] = m;
		vis[m] = true;
		dfs(1, m);
		vis[m] = false;
	}
	return 0;
}
4、HDU 3533 Escape

没有写,看不太懂题目。

5、HDU 1560 DNA sequence

题意:
给定N(N < 8)个序列,每个序列的长度在1~5之中,求一个长度最短的序列,N个序列都是这个序列的子序列。输出该序列的长度。

题解:
没有想到搜索怎么做,用dp做的。
数组a[8]:存储每个序列已经匹配的长度
数组 l[8]:存储每个序列的总长度
遍历A、C、G、T,看是否能否跟N序列中的元素匹配,则该序列已经匹配的长度加一,继续dp
使用一个栈存储匹配的序列,最后回溯。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 8;
const int L = 6;
const char D[] = {'A', 'C', 'G', 'T'};

int t, n, l[N], a[N], f[L][L][L][L][L][L][L][L];
int stack[10000000], top = 0;
char s[N][L], vis[L][L][L][L][L][L][L][L];

int dp () {
	if (vis[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]]) {
		return f[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]];
	}
//	printf("%d %d %d %d %d %d %d %d\n", a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
	vis[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]] = true;
	int &ans = f[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]];
	ans = 100;
	int size = 0;
	for (int i = 0; i < 4; i++) {
		bool flag = false;
		for (int j = 0; j < n; j++)  {
			if (a[j] < l[j] && s[j][a[j]] == D[i]) {
				a[j]++;
				size++;
				flag = true;
				stack[++top] = j;
			}
		}
		if (flag) {
			ans = min(ans, dp() + 1);
			while (size != 0) {
				size--;
				a[stack[top--]]--;
			}
		}
	}
	return ans;
}

int main () {
	scanf("%d", &t);
	while (t--) {
		memset(vis, 0, sizeof(vis));
		memset(l, 0, sizeof(l));
		memset(a, 0, sizeof(a));
		scanf("%d", &n);
		for (int i = 0; i < n; i++) {
			getchar();
			scanf("%s", s[i]);
			l[i] = strlen(s[i]);
		}
		vis[l[0]][l[1]][l[2]][l[3]][l[4]][l[5]][l[6]][l[7]] = true;
		f[l[0]][l[1]][l[2]][l[3]][l[4]][l[5]][l[6]][l[7]] = 0;
		printf("%d\n", dp());
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值