图论基础——最短路问题

一。单源最短路问题1。(Bellman-Ford)

1.当图为DAG时,把图拓扑排序一下,然后用递推关系d[i] = d[j] + e(i, j)。
2.当图有圈且不存在负圈时,无法一下子全部递推出来,我们用一个循环套住递推式子,循环在每个节点都算出来时退出。
struct edge{int from, to, cost;};

edge es[MAX_E];

int d[MAX_V];
int V, E;

从s到所有点的最短距离
void shortest_path(int s)
{
    for(int i = 0; i < V; i++)
        d[i] = INF;

    d[s] = 0;

    while(true)
    {
        bool update = false;

        for(int i = 0; i < E; i++){
            edge e = es[i];
            if(d[e.from] != INF && d[e.to] > d[e.from] + e.cost){
                d[e.to] = d[e.from] + e.cost;
                update = true;
            }
        }
        if(!update)
            break;
    }
}
用这个算法能检验负圈:
用Bellman_Ford检查负圈
bool find_negative_loop()
{
    memset(d, 0, sizeof(d));

    for(int i = 0; i < V; i++)
        for(int j = 0; j < E; j++)
    {
        edge e = es[j];
        if(d[e.to] > d[e.from] + e.cost){
            d[e.to] = d[e.from] + e.cost;

            if(i == v - 1)
                return true;
        }
    }

    return false;
}

二。单源最短路问题2.(Dijkstra)

在上一个算法中,如果d[i]没有最短,那么由d[i]递推出来的都是无用的。而且,如果d[i]没有变化,每次循环也要检查一遍所有i链接的顶点。Dijkstra算法则是从已经确定是最短的顶点往后推。已确定的距离最短的点就是没用过的点中d[i]最小的i,这些d[i]一定是有已经确定的最短点推出来的。d数组初始化为足够大的数字。

int cost[MAX_V][MAX_V];
int d[MAX_V];
bool used[MAX_V];
int V;

void dijkstra()
{
    fill(d, d + V, INF);
    fill(used, used + V, false);
    d[s] = 0;


    while(true)
    {
        int v = -1;

        for(int u = 0; u < V; u++){
            if(!used[u] && (v == -1 || d[u] < d[v]) )
                v = u;
        }

        if(v == -1)
            break;

        used[v] = true;

        for(int u = 0; u < V; u++)
            d[u] = min(d[u], d[v] + cost[v][u]);
    }
}

找最小点的操作可以用堆来优化。

堆优化
struct edge{int to, cost; };
typedef pair P;

int V;
vector G[MAX_V];
int d[MAX_V];

void dijkstra(int s)
{
    priority_queue, greater

> que; fill(d, d + V, INF); d[s] = 0; que.push(P(0, s)); while(!que.empty()) { P p = que.top();que.pop(); int v = p.second; if(d[v] < p.first) continue; for(int i = 0; i < G[v].size(); i++){ edge e = G[v][i]; if(d[e.to] > d[v] + e.cost){ d[e.to] = d[v] + e.cost; que.push(P(d[e.to], e.to)); } } } }

如果存在负边,要用第一种。

三。任意两点间的最短路(Floyd_Warshall)

用DP解决。这和走不同路捡不同数量苹果差不多。

如果顶点上限为k:逗号左边是不含k,右边是含k。
状态转移方程:c[i, j, k]=min{c[i, j, k-1], c [i, k, k-1]+c [k, j, k-1]},k>0。

实现时不用为上限k多给数组开一维

int d[MAX_V][MAX_V];
int V;

void warshall_floyd()
{
    for(int k = 0; k < V; k++)
        for(int i = 0; i < V; i++)
            for(int j = 0; j < V; j++)
            d[i][j] = min(d[i, j], d[i][k] + d[k][j]);
}

一定先递增枚举k,这样k才能从k-1转移过来。



你可能感兴趣的:(挑战程序设计竞赛)