priority_queue
八数码 HDU 1043 题目链接
思路
利用A*算法 + 康托展开压缩空间
康托展开传送门
A*算法传送门
#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 5;
struct node{
int p[4][4];
int x,y;
int g,h;
int hash_num;
bool operator < (const node& t) const{ /// A*的启发函数
if (h == t.h) return g > t.g;
return h > t.h;
}
}now;
struct path{
int pre;
char ch;
}pa[N];
int vis[N];
int fac[9] = {1,1,2,6,24,120,720,5040,40320};
int to[4][2] = {1,0, -1,0, 0,1, 0,-1};
char dir[9] = "durl";
int Hash(const node& t) /// 康托展开,压缩空间
{
int ans = 0;
for (int i = 0; i < 9; i++){
int k = 0;
for (int j = i + 1; j < 9; j++){
if (t.p[i / 3][i % 3] > t.p[j / 3][j % 3]) k++;
}
ans += fac[8 - i] * k;
}
return ans;
}
int calc(const node& t) /// 评估函数,计算到原点的代价
{
int ans = 0;
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++){
if (t.p[i][j] == 9) continue;
ans += abs((t.p[i][j] - 1) / 3 - i) + abs((t.p[i][j] - 1) % 3 - j);
}
}
return ans;
}
void print(int x)
{
if (pa[x].pre == -1) return ;
print(pa[x].pre);
printf("%c",pa[x].ch);
}
void A_star()
{
priority_queue<node> q;
memset(vis,0,sizeof(vis));
if (Hash(now) == 0){
cout << '\n';
return ;
}
vis[Hash(now)] = 1;
now.g = 0;
now.hash_num = Hash(now);
pa[Hash(now)].pre = -1;
q.push(now);
while (!q.empty()){
now = q.top();
q.pop();
node nex;
for (int i = 0; i < 4; i++){
int xx = now.x + to[i][0];
int yy = now.y + to[i][1];
if (xx < 0 || xx >= 3 || yy < 0 || yy >= 3) continue;
nex = now;
swap(nex.p[xx][yy],nex.p[now.x][now.y]);
int res = Hash(nex);
if (vis[res]) continue;
nex.g++;
nex.hash_num = res;
nex.h = calc(nex);
nex.x = xx;
nex.y = yy;
pa[res].pre = now.hash_num;
pa[res].ch = dir[i];
if (res == 0){
print(res);
cout << '\n';
return ;
}
vis[res] = 1;
q.push(nex);
}
}
}
int main()
{
char str[44];
while (cin.getline(str,40)){
int k = 0;
for (int i = 0; str[i] != '\0'; i++){
if (str[i] == ' ') continue;
if (str[i] == 'x'){
now.x= k / 3;
now.y = k % 3;
now.p[k / 3][k % 3] = 9;
k++;
}
else {
now.p[k / 3][k % 3] = str[i] - '0';
k++;
}
}
k = 0;
/// 判断逆序数
for (int i = 0; i < 9; i++){
if (now.p[i / 3][i % 3] == 9) continue;
for (int j = 0; j < i; j++){
if (now.p[j / 3][j % 3] == 9) continue;
if (now.p[i / 3][i % 3] < now.p[j / 3][j % 3]) k++;
}
}
if (k & 1) printf("unsolvable\n");
else A_star();
}
return 0;
}
八数码2 HDU 3567 题目链接
题意:经典八数码问题,将八数码从状态A转化到状态B,问最短且字典序最小的解法。
思路
#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 5;
struct node{
int x,y;
char Map[4][4];
node(){}
node(string str){
for (int i = 0; i < 9; i++){
if (str[i] == 'X'){
x = i / 3;
y = i % 3;
Map[i / 3][i % 3] = '9';
}
else Map[i / 3][i % 3] = str[i];
}
}
}now;
int fac[9] = {1,1,2,6,24,120,720,5040,40320};
int to[4][2]={{1,0},{0,-1},{0,1},{-1,0}};
char dir[5]="dlru";
int pre[11][N];
char ans[11][N];
char str[44];
bool vis[N];
int get_hash(const node& p)
{
int ans = 0;
for (int i = 0; i < 9; i++){
int k = 0;
for (int j = i + 1; j < 9; j++){
if (p.Map[i / 3][i % 3] > p.Map[j / 3][j % 3]) k++;
}
ans += fac[8 - i] * k;
}
return ans;
}
void A_star(int x)
{
memset(pre[x],-1,sizeof(pre[x]));
memset(vis,false,sizeof(vis));
queue<node> q;
q.push(now);
vis[get_hash(now)] = true;
while (!q.empty()){
now = q.front();
q.pop();
node nex;
int now_num = get_hash(now);
for (int i = 0; i < 4; i++){
int xx = now.x + to[i][0];
int yy = now.y + to[i][1];
if (xx < 0 || xx >= 3 || yy < 0 || yy >= 3) continue;
nex = now;
nex.Map[now.x][now.y] = nex.Map[xx][yy];
nex.Map[xx][yy] = '9';
int res = get_hash(nex);
if (vis[res]) continue;
nex.x = xx;
nex.y = yy;
pre[x][res] = now_num;
ans[x][res] = dir[i];
vis[res] = true;
q.push(nex);
}
}
}
int main()
{
now = node("X12345678");
A_star(0);
now = node("1X2345678");
A_star(1);
now = node("12X345678");
A_star(2);
now = node("123X45678");
A_star(3);
now = node("1234X5678");
A_star(4);
now = node("12345X678");
A_star(5);
now = node("123456X78");
A_star(6);
now = node("1234567X8");
A_star(7);
now = node("12345678X");
A_star(8);
int t,Case = 1;
scanf("%d",&t);
while (t--){
int num[11];
int index,k = 0;
scanf("%s",str);
for (int i = 0; i < 9; i++){
if (str[i] == 'X') index = i;
else num[str[i] - '0'] = k++;
}
scanf("%s",str);
for (int i = 0; i < 9; i++){
if (str[i] == 'X') continue;
str[i] = num[str[i] - '0'] + '1';
}
printf("str = %s\n",str);
now = node(str);
int res = get_hash(now);
string ss = "";
while(res != -1){
ss += ans[index][res];
res = pre[index][res];
}
ss.pop_back();
reverse(ss.begin(),ss.end());
printf("Case %d: %d\n",Case++,(int)ss.size());
for (auto k : ss) cout << k;
cout << '\n';
}
return 0;
}
Escape HDU 3533 题目链接
题意
一个地图上有一些炮台,会间隔发射炮弹,炮台可以挡住炮弹,问从(0,0)->(n,m)需要多少时间
思路
预处理每一秒炮弹会出现在哪里
然后可以有上下左右和不动五种操作
使用A*算法
错误点
三个bool数组开成int就会超出内存
#include <bits/stdc++.h>
using namespace std;
const int N = 111;
struct node{
int ch,v,t,x,y;
}s[N];
struct point{
int x,y,t,g,h;
bool operator < (const point& p) const{
if (h == p.h) return g > p.g;
return h > p.h;
}
};
bool vis[N][N][N * 10],g[N][N][N * 10]; /// xyt;
bool G[N][N];
int to[5][2] = {1,0,-1,0,0,1,0,-1,0,0};
int n,m,k,d;
void create_map()
{
for (int i = 0; i < k; i++){
for (int j = 0; j <= d; j += s[i].t){
for (int l = 1;; l++){ /// l从1开始遍历
int xx = s[i].x + to[s[i].ch][0] * l;
int yy = s[i].y + to[s[i].ch][1] * l;
if (xx < 0 || xx > n || yy < 0 || yy > m || G[xx][yy]) break;
if (l % s[i].v == 0) {
g[xx][yy][j + l / s[i].v] = 1;
}
}
}
}
}
void bfs()
{
priority_queue<point> q;
while(!q.empty()) q.pop();
point now,nex;
now.x = now.y = now.t = 0;
memset(vis,0,sizeof(vis));
vis[0][0][0] = 1;
q.push(now);
while(!q.empty()){
now = q.top();
q.pop();
if (now.x == n && now.y == m){
printf("%d\n",now.t);
return ;
}
if (now.t > d) break;
for (int i = 0; i < 5; i++){
int xx = now.x + to[i][0];
int yy = now.y + to[i][1];
if (xx < 0 || xx > n || yy < 0 || yy > m) continue;
if (vis[xx][yy][now.t + 1]) continue;
if (g[xx][yy][now.t + 1]) continue;
if (G[xx][yy]) continue;
nex.x = xx;
nex.y = yy;
nex.t = now.t + 1;
nex.g = abs(xx - n) + abs(yy - m);
nex.h = nex.g + nex.t;
vis[xx][yy][nex.t] = 1;
q.push(nex);
}
}
printf("Bad luck!\n");
}
int main()
{
while(scanf("%d %d %d %d",&n,&m,&k,&d) == 4){
memset(G,0,sizeof(G));
for (int i = 0; i < k; i++){
char ch;
scanf(" %c %d %d %d %d",&ch,&s[i].t,&s[i].v,&s[i].x,&s[i].y);
if (ch == 'S') s[i].ch = 0;
else if (ch == 'N') s[i].ch = 1;
else if (ch == 'E') s[i].ch = 2;
else if (ch == 'W') s[i].ch = 3;
G[s[i].x][s[i].y] = 1;
}
memset(g,0,sizeof(g));
create_map();
bfs();
}
return 0;
}
DNA HDU 1560 题目链接
题意
给你N个DNA序列,找到最短的公共DNA序列
思路
迭代深搜(IDA*):一步步加深深度,找到结果就是最短的
#include <bits/stdc++.h>
using namespace std ;
const int N = 11;
int pos[N];
int len[N];
char ans[N];
int n;
char str[N][N];
char DNA[5] = {'A','C','G','T'};
int limit;
int calc()
{
int mx = 0;
for (int i = 0; i < n; i++){
mx = max(mx,len[i] - pos[i]);
}
return mx;
}
bool dfs(int deep)
{
if (calc() + deep > limit) return false;
if (!calc()) return true;
int tem[11];
memcpy(tem,pos,sizeof(pos));
for (int i = 0; i < 4; i++){
bool flag = false;
for (int j = 0; j < n; j++){
if (str[j][pos[j]] == DNA[i]){
flag = true;
pos[j]++;
// ans[deep] = DNA[i];
}
}
if (flag){
if (dfs(deep + 1)) return true;
memcpy(pos,tem,sizeof(tem));
}
}
return false;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
limit = 0;
for (int i = 0; i < n; i++){
scanf("%s",str[i]);
len[i] = strlen(str[i]);
limit = max(limit,len[i]);
}
memset(pos,0,sizeof(pos));
while(true){
if (dfs(0)) break;
limit++;
}
printf("%d\n",limit);
// cout << ans << endl;
}
return 0;
}
转魔方 ZOJ 2477 题目链接
题意
给你一个摊开的魔方,问你是否能在五步之内复原,如果能,输出对应的步骤。
操作:可以顺逆时针转动(顺时针输出1,逆时针输出-1)
思路
迭代加深,一共有12种操作,每次只改变20个位置,20个位置可分成四块,每块五个,预处理这些位置即可
#include <iostream>
#include <cstdio>
#include <utility>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 66;
int pos[6][9] = {
1,2,3,4,5,6,7,8,9,
10,11,12,22,23,24,34,35,36,
13,14,15,25,26,27,37,38,39,
16,17,18,28,29,30,40,41,42,
19,20,21,31,32,33,43,44,45,
46,47,48,49,50,51,52,53,54
};
int to[12][20] = {
1,4,7,11,12, 45,33,21,22,10, 46,49,52,35,34, 13,25,37,24,36, /// r(0)逆
1,4,7,11,12, 13,25,37,24,36, 46,49,52,35,34, 45,33,21,22,10,
7,8,9,14,15, 36,24,12,25,13, 48,47,46,38,37, 16,28,40,27,39, /// g(1)逆
7,8,9,14,15, 16,28,40,27,39, 48,47,46,38,37, 36,24,12,25,13, /// 顺
9,6,3,17,18, 39,27,15,28,16, 54,51,48,41,40, 19,31,43,30,42,
9,6,3,17,18, 19,31,43,30,42, 54,51,48,41,40, 39,27,15,28,16, /// b(2)顺
3,2,1,20,21, 42,30,18,31,19, 52,53,54,44,43, 10,22,34,33,45, /// o(3)逆
3,2,1,20,21, 10,22,34,33,45, 52,53,54,44,43, 42,30,18,31,19,
21,20,19,2,3, 12,11,10,4,1, 15,14,13,8,7, 18,17,16,6,9, /// w(4)逆
21,20,19,2,3, 18,17,16,6,9, 15,14,13,8,7, 12,11,10,4,1,
37,38,39,47,48, 34,35,36,49,46, 43,44,45,53,52, 40,41,42,51,54,/// y(5)逆
37,38,39,47,48, 40,41,42,51,54, 43,44,45,53,52, 34,35,36,49,46
};
char str[N];
int ans[6][2];
int limit;
bool judge()
{
for (int i = 0; i < 6; i++){
for (int j = 0; j < 9; j++){
if (str[pos[i][j]] != str[pos[i][0]]) return false;
}
}
return true;
}
void run(int x)
{
for (int i = 0; i < 5; i++){
char tem;
tem = str[to[x][19 - i]];
str[to[x][19 - i]] = str[to[x][14 - i]];
str[to[x][14 - i]] = str[to[x][9 - i]];
str[to[x][9 - i]] = str[to[x][4 - i]];
str[to[x][4 - i]] = tem;
}
}
bool dfs(int deep)
{
if (judge()) return true;
if (deep == limit) return false;
char tem[66];
memcpy(tem,str,sizeof(str));
for (int i = 0; i < 12; i++){
run(i);
ans[deep][0] = i / 2;
ans[deep][1] = (i & 1) ? 1 : -1;
if (dfs(deep + 1)) return true;
memcpy(str,tem,sizeof(tem));
}
return false;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
for (int i = 1; i <= 54; i++){
scanf(" %c",&str[i]);
}
for (limit = 0; limit <= 5; limit++){
memset(ans,0,sizeof(ans));
if (dfs(0)) break;
}
if (limit > 5) {
cout << -1 << endl;
continue;
}
cout << limit << endl;
for (int i = 0; i < limit; i++){
cout << ans[i][0] << " " << ans[i][1] << endl;
}
}
return 0;
}
HDU 1067 题目链接
题意
将28张牌按顺序摆好
思路
变成字符串使用map(或者写个hash映射也行)
最后一列的1不能改成0
因为这两个数组映射一样(找不到原因)
char wenti1[35] = {
11, 26 ,0, 13 ,44, 45, 24, 42,
21 ,17, 0 ,23 ,25 ,0 ,36 ,0,
31 ,46 ,34,14 ,12 ,37 ,32, 47,
41 ,16 ,43 ,27, 35 ,22, 33 ,15
};
char wenti2[35] = {
11 ,26 ,0 ,13, 44 ,0, 24 ,42,
21 ,17 ,45 ,23, 25, 0 ,36, 0,
31 ,46 ,34 ,14 ,12, 37 ,32 ,47,
41 ,16 ,43 ,27 ,35 ,22 ,33 ,15
};
#include <bits/stdc++.h>
using namespace std;
const int N = 888;
struct node{
int cnt;
char g[35];
}now,nex;
char pos[35] = {
11,12,13,14,15,16,17,1,
21,22,23,24,25,26,27,1,
31,32,33,34,35,36,37,1,
41,42,43,44,45,46,47,1
};
int bfs()
{
queue<node> q;
while(!q.empty()) q.pop();
map<string,int> mp;
mp.clear();
mp[now.g] = 1;
now.cnt = 0;
q.push(now);
while(!q.empty()){
now = q.front();
q.pop();
if (strcmp(now.g,pos) == 0) return now.cnt;
for (int i = 1; i < 32; i++){
if (now.g[i] == 1 && now.g[i - 1] != 1){
for (int j = 1; j < 32; j++){
if (now.g[j] == now.g[i - 1] + 1){
nex = now;
nex.g[i] = nex.g[j];
nex.g[j] = 1;
if (mp[nex.g]) continue;
mp[nex.g] = 1;
nex.cnt++;
q.push(nex);
break;
}
}
}
}
}
return -1;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
now.g[0] = 11;
now.g[8] = 21;
now.g[16] = 31;
now.g[24] = 41;
for (int i = 0; i < 4; i++){
for (int j = 1; j < 8; j++){
int x;
scanf("%d",&x);
if (x % 10 == 1) x = 1;
now.g[i * 8 + j] = x;
}
}
printf("%d\n",bfs());
}
return 0;
}
HDU 3001 题目链接
题意
n(n<=10)个城市要旅游,要每个城市都去一次,不能超过两次,问最小花费
思路
因为n比较小,可以用状态压缩,三进制表示去过0,1,2次
#include <bits/stdc++.h>
using namespace std;
const int N = 22;
const int INF = 0x3f3f3f3f;
/**
dp[i][j] 代表 i为已走过的点(三进制) ~ j为终点 的代价
dp[i][j] = min(dp[i][j],dp[i - bit[k]][x] + g[x][j]);
k代表i中的其中一个走过的点
x 为 i - bit[k]中已走过的点
*/
int dp[60000][N];
int pos[N];
int g[N][N];
int num;
int bit[13] = {0,1,3,9,27,81,243,729,2187,6561,19683,59049};
void calc(int x)
{
num = 0;
while(x){
pos[++num] = x % 3;
x /= 3;
}
}
int main()
{
int n,m;
while(scanf("%d %d",&n,&m) == 2){
memset(g,0x3f,sizeof(g));
for (int i = 0; i < m; i++){
int u,v,len;
scanf("%d %d %d",&u,&v,&len);
g[u][v] = min(g[u][v],len);
g[v][u] = min(g[v][u],len);
}
memset(dp,0x3f,sizeof(dp));
for (int i = 1; i <= n; i++){
dp[bit[i]][i] = 0;
}
int ans = INF;
for (int i = 1; i < bit[n + 1]; i++){
memset(pos,0,sizeof(pos));
calc(i);
bool flag = true;
for (int j = 1; j <= n; j++){
if (!pos[j]) {
flag = false;
continue;
}
int tem = i - bit[j];
for (int k = 1; k <= n; k++){
if (k == j || !pos[k]) continue;
dp[i][j] = min(dp[i][j],dp[tem][k] + g[k][j]);
}
}
if (!flag) continue;
for (int j = 1; j <= n; j++){
ans = min(ans,dp[i][j]);
}
}
if (ans == INF) cout << -1 << endl;
else cout << ans << endl;
}
return 0;
}