A*入门两题——第k短路问题 && [SCOI2005]骑士精神

7 篇文章 0 订阅
1 篇文章 0 订阅

A*算法网上有很多资料,在这里不想写…

最主要的是估价函数f,估的是当前状态到最终状态的代价。

f 为真正的代价

f<f ,能搜到最优解,但是复杂度不优。

f=f ,能搜到最优解,且复杂度最优。

f>f ,有可能搜不到最优解,但是跑得快。

第k短路问题:

其核心在于预处理出所有点到终点的最短路h。

设g为起点到这个点的长度。

f = g +h

按f的大小从小到大取,用堆维护。

每次取出来就将它相邻的点更新。

这样的正确性你可以想象是把小于(和一部分等于)第k短路长度的路径全部找了出来,复杂度取决于这个再乘上数据结构的复杂度。

Code:

#include<set>
#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int N = 1e4 + 5, M = 2e5 + 5;

int n, m, x, y, z, s, t, k;

struct edge {
    int tt, next[M], to[M], final[N], w[M];
    void link(int x, int y, int z) {next[++ tt] = final[x], to[tt] = y, w[tt] = z, final[x] = tt;}
} e1, e2;

int d[M * 50], dis[N], bz[N];

void Spfa() {
    memset(dis, 127, sizeof dis);
    dis[t] = 0; d[1] = t; d[0] = 1; bz[t] = 1;
    fo(i, 1, d[0]) {
        int x = d[i];
        for(int j = e2.final[x]; j; j = e2.next[j]) {
            int y = e2.to[j], z = e2.w[j];
            if(dis[x] + z < dis[y]) {
                dis[y] = dis[x] + z;
                if(!bz[y]) bz[y] = 1, d[++ d[0]] = y;
            }
        }
        bz[x] = 0;
    }
}

struct node {
    int x, g, h;
    node (int _x, int _g, int _h) {x = _x, g = _g, h = _h;}
    node () {}
};

bool operator <(node a, node b) {return a.g + a.h < b.g + b.h;}

int tot, ts, ans;

multiset<node> st;

int main() {
    scanf("%d %d %d", &n, &m, &k);
    fo(i, 1, m) {
        scanf("%d %d %d", &x, &y, &z);
        e1.link(x, y, z); e2.link(y, x, z);
    }
    s = 1; t = n;
    Spfa();
    st.insert(node(s, 0, dis[s]));
    while(1) {
        ts ++;
        node p = *st.begin();
        int x = p.x;
        if(x == t) {
            if(p.g > ans) tot ++, ans = p.g;
        }
        if(tot == k) {
            printf("%d", p.g); return 0;
        }
        st.erase(st.begin());
        for(int j = e1.final[x]; j; j = e1.next[j]) {
            int y = e1.to[j], z = e1.w[j];
            st.insert(node(y, p.g + z, dis[y]));
        }
    }
}

[SCOI2005]骑士精神:

链接.
A*裸题。

双向广搜应该是可以过的,但是状态的压缩有些麻烦。

深搜的话加个剪枝就行了。

设当前状态已有步数为g,和目标状态的不同数为h, 则至少还需要h-1步才能到。

因此如果有g+h-1>已知的ans,退了就行了。

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

int T, ans;
int move[8][2] = {{1, 2}, {-1, 2}, {1, -2}, {-1, -2}, {2, 1}, {-2, 1}, {2, -1}, {-2, -1}};
char s[6][6], s2[6][6];

void dg(int g) {
    int sum = 0, x, y;
    fo(i, 1, 5) fo(j, 1, 5) {
        sum += s[i][j] != s2[i][j];
        if(s[i][j] == '*') x = i, y = j;
    }
    if(g + sum > ans) return;
    if(!sum) ans = g;
    fo(i, 0, 7) {
        int l = x + move[i][0], r = y + move[i][1];
        if(l > 0 && l <= 5 && r > 0 && r <= 5) {
            swap(s[l][r], s[x][y]);
            dg(g + 1);
            swap(s[l][r], s[x][y]);
        }
    }
}

int main() {
    freopen("a.in", "r", stdin);

    fo(i, 1, 5) s2[1][i] = '1';
    s2[2][1] = '0'; fo(i, 2, 5) s2[2][i] = '1';
    s2[3][1] = s2[3][2] = '0'; s2[3][3] = '*'; s2[3][4] = s2[3][5] = '1';
    s2[4][5] = '1'; fo(i, 1, 4) s2[4][i] = '0';
    fo(i, 1, 5) s2[5][i] = '0';

    for(scanf("%d", &T); T; T --) {
        fo(i, 1, 5) scanf("%s", s[i] + 1);
        ans = 16;
        dg(0);
        if(ans == 16) printf("-1\n"); else printf("%d\n", ans);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值