搜索进阶

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值