学习记录 -- A*,IDA*算法

A*算法:bfs + 启发式搜索(比普通搜索多一个估价函数)。

IDA* : 迭代加深 + dfs + 启发式搜索。

IDA*相较于A*:

1.不需要判重,不需要排序,利于深度剪枝。

2.空间需求减少:每个深度下实际上是一个深度优先搜索,不过深度有限制,使用 DFS 可以减小空间消耗。

3.重复搜索:即使前后两次搜索相差微小,回溯过程中每次深度变大都要再次从头搜索。

P1379 八数码难题

这题好像不怎么优化也能过,但我还是用了A*算法。

题目求给定的数组变成目标数组需要的最少操作数(操作:只能交换'0'和与它相邻的位置)。

由题意得出:估价函数=当前数组和目标数组在相同位置有多少个不同的+当前操作数。

重要的就是估价函数,其他跟bfs没差。

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

char df[4][4] = {"123", "804", "765"};  //目标数组

struct mair {
    char st[4][4];

    bool operator < (const mair& t) const {  //因为该类型要作为map的key,要求有序,所以需要重载
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if(st[i][j] != t.st[i][j]) return st[i][j] < t.st[i][j];
            }
        }
        return false;
    }
}nf;   //初始数组

int H(mair s) {   //除'0'外与目标数组在相同位置有多少个不同的(因为我加上'0'不相等的时候WA了)
    int ans = 0;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if(s.st[i][j] != df[i][j] && s.st[i][j] != '0') ans++;
        }
    }
    return ans;
}

int F(mair s, int t) {  //估价函数 : f = h + g, g 是当前步数
    return H(s) + t;
}

struct node {
    mair s;
    int t;

    bool operator <(const node& temp) const {
       return F(s, t) > F(temp.s, temp.t);
    }
};

priority_queue<node>q;  //搜索队列
map<mair, bool>vis;     //标记该数组是否出现

void bfs() {
    q.push({nf, 0});
    vis[nf] = true;
    while(!q.empty()) {
        node p = q.top();
        q.pop();
        if(H(p.s) == 0) {
            cout << p.t << endl;
            return;
        }
        int x = -1, y = -1;
        for (int i = 0; i < 3; i++) {   //找到空格
            for (int j = 0; j < 3; j++) {
                if(p.s.st[i][j] == '0') {
                    x = i, y = j;
                    break;
                }
            }
            if(x != -1) break; 
        }
        p.t++;
        for (int i = 0; i < 4; i++) {   
            int x1 = x + dx[i];
            int y1 = y + dy[i];
            if(x1 >= 0 && x1 < 3 && y1 >= 0 && y1 < 3) {
                swap(p.s.st[x1][y1], p.s.st[x][y]);  //交换
                if(!vis[p.s]) {
                    q.push(p);
                    vis[p.s] = true;
                } 
                swap(p.s.st[x][y], p.s.st[x1][y1]);  //换回来
            }
        }
    }
}

void solve() {
    string s;
    cin >> s;
    int x = 0, y = 0;
    for (int i = 0; i < s.size(); i++) {
        nf.st[x][y] = s[i];
        y++;
        if((i + 1) % 3 == 0) {
            x++;
            y = 0;
        }
    }
    bfs();
}

(这题用IDA*也可以做,这里就不写了)

P2324 [SCOI2005] 骑士精神

移动:骑士与空格交换。

跟八数码差不多,只是这题规定了在15步之内(像极了迭代加深IDA*,但是现在我用A*)。

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

char df[6][6] = {"11111", "01111", "00*11", "00001", "00000"};

struct mairx {
    char st[6][6];

    bool operator <(const mairx& t) const {
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                if(st[i][j] != t.st[i][j]) return st[i][j] > t.st[i][j];
            }
        }
        return false;
    }
};

int h(mairx x) {
    int cnt = 0;
    for (int i = 0; i < 5; i++) {    //跟八数码一样的h函数
        for (int j = 0; j < 5; j++) {
            if(x.st[i][j] != df[i][j] && x.st[i][j] != '*') cnt++;
        }
    }
    return cnt;
}

struct node {
    mairx s;
    int t;

    bool operator <(const node& temp) const {
        return t + h(s) > temp.t + h(temp.s);
    }
};

mairx nx;

void bfs() {
    priority_queue<node>q;
    map<mairx, bool>vis;
    q.push({nx, 0});
    vis[nx] = true;
    while(!q.empty()) {
        node p = q.top();
        q.pop();
        if(h(p.s) + p.t > 15) continue;  //若当前与目标数组不同个数+当前步数>15就不用继续搜了
        if(h(p.s) == 0) {
            cout << p.t << endl;
            return;
        }
        int x = -1, y;
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                if(p.s.st[i][j] == '*') {
                    x = i, y = j;
                    break;
                }
            }
            if(x != -1) break;
        }
        p.t++;
        for (int i = 0; i < 8; i++) {
            int x1 = x + dx[i];
            int y1 = y + dy[i];
            if(x1 >= 0 && x1 < 5 && y1 >= 0 && y1 < 5) {
                swap(p.s.st[x][y], p.s.st[x1][y1]);
                if(!vis[p.s]) {
                    q.push(p);
                    vis[p.s] = true;
                }
                swap(p.s.st[x][y], p.s.st[x1][y1]);
            }
        }
    }
    cout << -1 << endl;
}

void solve() {
    for (int i = 0; i < 5; i++) {
        cin >> nx.st[i];
    }
    bfs();
}

当然,我也用了IDA*,方法当然还是掌握的越多越好,不过这个提交跑的好像还没有我写的A*快。

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

char df[6][6] = {"11111", "01111", "00*11", "00001", "00000"};

struct mairx {
    char st[6][6];

    bool operator <(const mairx& t) const {
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                if(st[i][j] != t.st[i][j]) return st[i][j] > t.st[i][j];
            }
        }
        return false;
    }
};

int h(mairx x) {
    int cnt = 0;
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            if(x.st[i][j] != df[i][j] && x.st[i][j] != '*') cnt++;
        }
    }
    return cnt;
}

bool dfs(mairx s, int cnt, int cnt_e) {
    int h0 = h(s);
    if(h0 + cnt > cnt_e) return false;  //剪枝
    if(!h0) return true;

    int x = -1, y;
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            if(s.st[i][j] == '*') {
                x = i, y = j;
                break;
            }
        }
        if(x != -1) break;
    }
    for (int i = 0; i < 8; i++) {
        int x1 = x + dx[i];
        int y1 = y + dy[i];
        if(x1 >= 0 && x1 < 5 && y1 >= 0 && y1 < 5) {
            swap(s.st[x1][y1], s.st[x][y]);
            if(dfs(s, cnt + 1, cnt_e)) {
                return true;
            }
            swap(s.st[x1][y1], s.st[x][y]);
        }
    } 
    return false;
}

void solve() {
    mairx nx;
    for (int i = 0; i < 5; i++) {
        cin >> nx.st[i];
    }
    for (int i = 0; i <= 15; i++) {
        if(dfs(nx, 0, i)) {
            cout << i << endl;
            return;
        }
    }
    cout << -1 << endl;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akb000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值