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