qbzt day2 晚上(竟然还有晚上)

内容提要

搜索

拓展欧几里得

逆元


先是搜索

A*

 

有几个数组

g 当前点到根节点的深度

h 当前点到终点理想的最优情况需要走几步

f  f=g+h

A*就是把所有的f从小到大排序

启发式搜索相较于其他的搜索的优势在于引入了一个启发式函数f(x) = g(x) +h(x)

g*(x) : 从 S 到 x 的理论最近距离

g(x) : 对 S 到 x 对于 g*(x) 的估计

f*(x) : 从 x 到 T 的理论最近距离,F*(x)=g*(x)+h*(x)

f(x) : 从 x 到 T 对于 f*(x) 的估计,F(x)=g(x)+h(x)

可以理解为:带*的是开挂的,不带*的是真实的

当满足 f(x) <= f*(x) 时,总能找到最优解

 

和 BFS 几乎一样,只是每次都弹出当前局面中f(x)最小的那个局面进行扩展

——故需要维护一个优先队列(小根堆)

——使用系统的priority_queue<>即可

当 f(x) = g(x) + h(x) 中 h(x) = 0 即失去了启发式函数,则变为Breath First Search

当 f(x) = g(x) + h(x) 中 g(x) = 0 则变为 Best First Search

当第一次到终点的时候就输出g(x)就可以了

 

例题:八数码

如何告诉计算机某种情况已经到达过呢:

如何做到将一个 1~n 的排列与一个整数做一一对应?

或者更直白的:

如何求出字典序第 k 的排列?

如何求出一个排列在字典序中排第几?

这样数组只需要开9!=36880

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
const int sizeofpoint=1024;
const int sizeofedge=262144;

int N, M;
int S, T, K;
int d[sizeofpoint], t[sizeofpoint];

struct node
{
    int u; int g;

    inline node(int _u, int _g):u(_u), g(_g) {}
};
inline bool operator > (const node & , const node & );

struct edge
{
    int point; int dist;
    edge * next;
};
inline edge * newedge(int, int, edge * );
inline void link(int, int, int);
edge memory[sizeofedge], * port=memory;
edge * e[sizeofpoint], * f[sizeofpoint];
std::priority_queue<node, std::vector<node>, std::greater<node> > h;

inline int getint();
inline void dijkstra(int);
inline int Astar();

int main()
{
    N=getint(), M=getint();
    for (int i=1;i<=M;i++)
    {
        int u=getint(), v=getint(), d=getint();
        link(u, v, d);
    }
    S=getint(), T=getint(), K=getint();
    dijkstra(T);

    if (d[S]==-1)
    {
        puts("-1");
        return 0;
    }

    K+=S==T;

    printf("%d
", Astar());

    return 0;
}

inline bool operator > (const node & u, const node & v)
{
    return (u.g+d[u.u])>(v.g+d[v.u]);
}

inline edge * newedge(int point, int dist, edge * next)
{
    edge * ret=port++;
    ret->point=point, ret->dist=dist, ret->next=next;
    return ret;
}
inline void link(int u, int v, int d)
{
    e[v]=newedge(u, d, e[v]);
    f[u]=newedge(v, d, f[u]);
}

inline int getint()
{
    register int num=0;
    register char ch;
    do ch=getchar(); while (ch<'0' || ch>'9');
    do num=num*10+ch-'0', ch=getchar(); while (ch>='0' && ch<='9');
    return num;
}
inline void dijkstra(int S)
{
    static int q[sizeofedge];
    static bool b[sizeofpoint];
    int l=0, r=0;

    memset(d, 0xFF, sizeof(d)), d[S]=0;
    for (q[r++]=S, b[S]=true;l<r;l++)
    {
        int u=q[l]; b[u]=false;
        for (edge * i=e[u];i;i=i->next) if (d[i->point]==-1 || d[u]+i->dist<d[i->point])
        {
            d[i->point]=d[u]+i->dist;
            if (!b[i->point])
            {
                b[i->point]=true;
                q[r++]=i->point;
            }
        }
    }
}
inline int Astar()
{
    h.push(node(S, 0));
    while (!h.empty())
    {
        node p=h.top(); h.pop();
        ++t[p.u];
        if (p.u==T && t[T]==K)
            return p.g;
        if (t[p.u]>K)
            continue;
        for (edge * i=f[p.u];i;i=i->next)
            h.push(node(i->point, p.g+i->dist));
    }
    return -1;
}

 

IDA*

 

g:从根节点往下几步

 

h:步数

 

 

 

g+h>d return

 

双向A*?双向IDA*?

 

h(x)>h*(x)?

 

事实上h(x)与h*(x)的关系隐形决定了A*的运行速度与准确度

 

h(x)越接近h*(x)跑得越快

 

 

 

拓展欧几里得

 

裴蜀定理:(x, y) = d ===> 存在无限多组整数 a,b:ax + by = d

 

 

 

证明:计算机竞赛不需要证明

 

 

 

扩展欧几里得算法可以帮助我们计算出 (x, y) = d 时一组 a,b:

 

 

什么?你问为什么?这么短的代码你背下来不就好了嘛?

 

扩展欧几里得算法有什么用呢?——计算逆元

 

逆元的定义:如果 x 对 p 有一个逆元 y,则 x * y == 1 (mod p)

 

x 对一个数 p 有逆元当且仅当 (x, p) = 1

 

意义:在取模意义下做除法

 

由裴蜀定理:存在 a, b 满足:ax + bp = 1

 

嗯……,等等,岂不是 ax == 1 (mod p)

计算组合数模p

中国剩余定理

 

问题、求余方程组 x = ai (mod pi)

 

不多说,背代码:

令 P = p1 * p2 * ... * pn

令 Pi = P / pi

令 Qi = Pi 对 pi 的逆元

 

则 x = sigma(ai * Pi * Qi)

 

原文地址:https://www.cnblogs.com/lcezych/p/11185568.html