图算法小结(prime与dijkstra对比)

(0)Dijstra 最短路径和prim最小生成树算法,神似,只是在更新dist时的if条件不同;主要是这种prime 的计算两个集合间的最小值的思想非常重要。

(1)某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多。这让行人很困扰。

现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。

Input

本题目包含多组数据,请处理到文件结束。

每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。

接下来是M行道路信息。每一行有三个整数A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路。

再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。

Output

对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1.

Sample Input

3 3

0 1 1

0 2 3

1 2 1

0 2

3 1

0 1 1

1 2

Sample Output

2

-1

// prime 和 dij算法的核心 —— 如何保证当前的最小边是最终的最小边,
// 上面试最小生成树的算法,下面是最短路径的算法了
#include <iostream>
#include <cstring>
using namespace std;

const int INF = 1000000;
const int MAX_SIZE = 200;
int map[MAX_SIZE][MAX_SIZE];
bool visited[MAX_SIZE];
int disit[MAX_SIZE]; //存储距离

//s开始位置,e结束位置
void dij_method(int s, int e, int N)
{
    // init the params of dij
    memset(visited, false, sizeof(visited));
    for (int i = 0; i < N; i++)
    {
        if (map[s][i] == -1)
            disit[i] = INF;
        else
            disit[i] = map[s][i];
    }
    // 从s 点开始计算
    visited[s] = true;
    for (int i = 1; i < N; i++)
    {
        int min = INF;
        int index = 0;
        // 找当前最小的 (这与prime一样,只是if条件更强了)
        for (int j = 0; j < N; j++)
        {
            if (!visited[j] && j != s && disit[j] != -1 && disit[j] < min)
            {
                min = disit[j];
                index = j;
            }
        }
        visited[index] = true;//顶点并入U集
        if (index == e) //如果已经达到终点
            break;
        // 调整更新目前的最短距离(这与这与prime一样,只是if条件不一样而已)
        for (int j = 0; j < N; j++)
        {
            if (!visited[j] && map[index][j] != -1
                    && (min + map[index][j]) < disit[j])
            {
                disit[j] = min + map[index][j];
            }
        }// end of for
    }// end of for
}

int main()
{
    int N, M;// 点数和记录数
    int ori, des, len;//
    int start, end;
    while (cin >> N >> M)
    {
        memset(map, -1, sizeof(map)); //-1表示不可达
        while (M--)
        {
            cin >> ori >> end >> len;
            //有可能有更小的路径
            if (map[ori][des] == -1 || len < map[ori][des])
            {
                map[ori][des] = len;
                map[des][ori] = len;
            }
        }
        // input and input start and end.
        cin >> start >> end;

        if (start == end)
        {
            cout << 0 << endl;
            continue;
        }
        // 调用无负权边的图(Dijkstra算法)
        dij_method(start, end, N);

        if (visited[end] && disit[end] < INF)
            cout << disit[end] << endl;
        else
            cout << -1 << endl;
    }
    return 0;
}

(2)* About:    有向图的Dijkstra算法实现

输入数据:

5

7

1 2 10

1 4 30

1 5 100

2 3 50

3 5 10

4 3 20

4 5 60

输出数据:

999999 10 999999 30 100

10 999999 50 999999 999999

999999 50 999999 20 10

30 999999 20 999999 60

100 999999 10 60 999999

源点到最后一个顶点的最短路径长度: 60

源点到最后一个顶点的路径为: 1 -> 4 -> 3 -> 5

#include <iostream>
using namespace std;

const int maxnum = 100;
const int maxint = 999999;

void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
{
    bool s[maxnum];    // 判断是否已存入该点到S集合中
    for(int i=1; i<=n; ++i)
    {
        dist[i] = c[v][i];
        s[i] = 0;     // 初始都未用过该点
        if(dist[i] == maxint)
            prev[i] = 0;
        else
            prev[i] = v;
    }
    dist[v] = 0;
    s[v] = 1;

    // 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
    // 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
    for(int i=2; i<=n; ++i)
    {
        int tmp = maxint;
        int u = v;
        // 找出当前未使用的点j的dist[j]最小值
        for(int j=1; j<=n; ++j)
            if((!s[j]) && dist[j]<tmp)
            {
                u = j;              // u保存当前邻接点中距离最小的点的号码
                tmp = dist[j];
            }
        s[u] = 1;    // 表示u点已存入S集合中

        // 更新dist
        for(int j=1; j<=n; ++j)
            if((!s[j]) && c[u][j]<maxint)
            {
                int newdist = dist[u] + c[u][j];
                if(newdist < dist[j])
                {
                    dist[j] = newdist;
                    prev[j] = u;
                }
            }
    }
}

void searchPath(int *prev,int v, int u)
{
    int que[maxnum];
    int tot = 1;
    que[tot] = u;
    tot++;
    int tmp = prev[u];
    while(tmp != v)
    {
        que[tot] = tmp;
        tot++;
        tmp = prev[tmp];
    }
    que[tot] = v;
    for(int i=tot; i>=1; --i)
        if(i != 1)
            cout << que[i] << " -> ";
        else
            cout << que[i] << endl;
}

int main()
{
    freopen("input.txt", "r", stdin);
    // 各数组都从下标1开始
    int dist[maxnum];     // 表示当前点到源点的最短路径长度
    int prev[maxnum];     // 记录当前点的前一个结点
    int c[maxnum][maxnum];   // 记录图的两点间路径长度
    int n, line;             // 图的结点数和路径数

    // 输入结点数
    cin >> n;
    // 输入路径数
    cin >> line;
    int p, q, len;          // 输入p, q两点及其路径长度

    // 初始化c[][]为maxint
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=n; ++j)
            c[i][j] = maxint;

    for(int i=1; i<=line; ++i)
    {
        cin >> p >> q >> len;
        if(len < c[p][q])       // 有重边
        {
            c[p][q] = len;      // p指向q
            c[q][p] = len;      // q指向p,这样表示无向图
        }
    }

    for(int i=1; i<=n; ++i)
        dist[i] = maxint;
    for(int i=1; i<=n; ++i)
    {
        for(int j=1; j<=n; ++j)
            printf("%8d", c[i][j]);
        printf("\n");
    }

    Dijkstra(n, 1, dist, prev, c);

    // 最短路径长度
    cout << "源点到最后一个顶点的最短路径长度: " << dist[n] << endl;

    // 路径
    cout << "源点到最后一个顶点的路径为: ";
    searchPath(prev, 1, n);
}

(3)

//poj2367题意: 知道一个数n, 然后n行,编号1到n, 每行输入几个数,
//该行的编号排在这几个数前面,输出一种符合要求的编号名次排序。
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;
const int MAXN = 105;

bool adj[MAXN][MAXN];// 林街边
int in_degree[MAXN];// 入度
int result[MAXN];// 结果顺序

void topo_sort(const int n)
{
    int i,j,k;
    memset(in_degree,0,sizeof(in_degree));
    for(i=1; i<=n; i++)   //寻找入度为零的节点
    {
        for(j=1; j<=n; j++)
        {
            if(adj[i][j])
                in_degree[j]++;
        }
    }// 可以在输入的时候就计算入度的数值

    for(i=1; i<=n; i++)// 这一个i表示个数的
    {
        for(j=1; j<=n; j++)
        {
            if(in_degree[j]==0)
            {
                k=j;
                break;
            }
        }
        in_degree[k]=-1;// 标志,已访问过
        result[i]=k;
        for(j=1; j<=n; j++)  //入度减一
        {
            if(adj[k][j])
            {
                in_degree[j]--;
            }
        }// end of for
    }// end of for
}

int main()
{
    int i,tem;
    int n;
    //init and input
    memset(adj,false,sizeof(adj));
    scanf("%d",&n);
    for(i=1; i<=n; i++)
    {
         while(scanf("%dtem",&tem),tem)
         {
             adj[i][tem]=true;
         }
    }
    // topo_sort
    topo_sort(n);
    for(i=1; i<=n; i++)
    {
        if(i==1)
             printf("%d",result[i]);
        else
            printf(" %d",result[i]);
    }
    printf("\n");
    return 0;
}

(4)

POJ 3249 拓扑排序+动态规划

分类: ACM 2012-06-11 13:38 812人阅读 评论(0) 收藏 举报

inputdelete存储struct

该题是让求在一个有向无环图中,从一个入度为 0 的节点出发,到一个出度为 0 的节点的最大的权值问题。我们可以使用广搜,但是会超时,上网查了一下解题报告,可以使用拓扑排序+动态规划来解决此问题:

dp[1] = max{ dp[2] + cost[1] , dp[3] + cost[1] };

dp[2] = cost[2] + dp[4];

dp[3] = cost[3] + dp[4];

dp[4] = cost[4];

dp[5] = cost[5] + dp[6];

dp[6] = cost[6];

在拓扑排序的过程中,是入度为0的节点先入队列,所以我们可以做一个逆置思考,是的 dp[i] 存储的是入度为零的节点到当前节点的最大权值和,而最后在出度为 0 的节点的 dp[i] 中搜索最大的权值。代码实现如下:

#include <iostream>
#include <queue>
#include <cstdio>

using namespace std;
#define N 100100
#define M 1000100
#define INF 1<<29

struct node
{
    int v;
    int next;
} edge[M];

int dp[N];  // 最大价值 存储 由 拓扑排序后 入度为0的节点到当前节点最大的 profit
int enext[N];   //记录节点 i 当前的边,由当前的边 推出 下一条边
int indegree[N];    //入度
int cost[N];    //价值
int res;
int idx;
std::queue<int > q;

int m,n,a,b,i,j;
void input()
{
    for(i=1;i<=n;i++)
    {
        scanf("%d",&cost[i]);
        dp[i] = -INF;
        indegree[i] = 0;
    }

    idx=0;

    //memset(dp,-INF,sizeof(int)*(n+10) );
    //memset(indegree,0,sizeof(int)*(n+10) );    //节点度数初始化为0
    memset(enext,-1,sizeof(enext) ); //说明当前节点只此一条边

    for(i=1;i<=m;i++)
    {
        scanf("%d %d",&a,&b);
        edge[idx].v = b;
        edge[idx].next = enext[a];
        enext[a] = idx++;
        indegree[b]++;
    }
    while( !q.empty() )
        q.pop();
    for( i=1;i<=n;i++)
        if( !indegree[i] )
            q.push(i),dp[i]=cost[i];
}

int dag()
{
    res = -INF;
    while( !q.empty() )
    {
        int cur = q.front();
        q.pop();
        bool flag = true;  //只有节点的出度为 0 的时候,我们才判断其是否有最大profit
        int nextid;
       // cout<<"cur : "<<cur<<endl;
        for( i=enext[cur] ; i != -1 ; i = edge[i].next) //遍历当前顶点的所有的边
        {
            flag = false;
            nextid =  edge[i].v ;

        //    cout<<"nextid : "<<nextid<<endl;
            if( dp[nextid] < cost[ nextid ] + dp[cur])
                dp[nextid] =  cost[nextid] + dp[cur];
            indegree[nextid]--;
            if( !indegree[nextid] )
            {
                q.push(nextid);
            }
        }
        if( flag && dp[cur] > res )
            res = dp[cur];
    }
    return res;
}

int main()
{
    while(scanf("%d %d",&n,&m) != EOF)
    {
        input();
        printf("%d\n",dag());
    }
    return 0;
}
时间: 2025-01-03 20:51:13

图算法小结(prime与dijkstra对比)的相关文章

(转)图算法单源最短路径Dijkstra算法(邻接表/邻接矩阵+优先队列STL)

一.前言 最短路径算法,顾名思义就是求解某点到某点的最短的距离.消耗.费用等等,有各种各样的描述,在地图上看,可以说是图上一个地点到达另外一个地点的最短的距离.比方说,我们把地图上的每一个城市想象成一个点,从一个城市到另一个城市的花费是不一样的.现在我们要从上海去往北京,需要考虑的是找到一条路线,使得从上海到北京的花费最小.有人可能首先会想到,飞机直达啊,这当然是时间消耗最小的方法,但是考虑到费用的高昂,这条线路甚至还不如上海到北京的高铁可取.更有甚者,假设国家开通了从上海到西藏,再从西藏到兰州

图算法小结

一:起因 (1)关于图的算法一般是比较复杂的,自己在这方面也是比较弱的,首先是图的存储问题 和 遍历问题: 存储分为两种,邻接矩阵 和 临街表:遍历分为DFS 和 BFS两种,非常类似于二叉树的先跟遍历和层次遍历. (2)图在实际应用中是非常广泛的,这与万物归一,万物相连的理论是一致的,两个物体之间有着千丝万缕的联系,我们成这种联系建立的网络为图(带权图):联系的强弱为边的权重. (3)图的一些复杂的算法,也是建立在两种遍历图的思想的基础上进行的,所以我们先从简单的谈起. 二:代码示例 (1)最

C#面向对象基础(二)

在对比中学习,找出知识的相似点和不同点,是学习一件快乐的事,也是高效学习的一种方法. 在学习知识的过 程中,我们都在不断地去联想,去设想,去假设,去推理,正因为这样才产生了很多的疑问,等这些疑问解决掉了, 或者解决的差不多的时候,就突然有了一种感慨:原来是这样! 在这里,我把C#中比较有意思的具有鲜明对比色彩的4种知识点进行了比较. 1.分支技术 分支是控制下一步要执行哪行代码的过程.       三元运算符 <test>?<resultIfTrue>:<resultIfFa

(UML总结一)四种关系

UML统一建模语言是用来设计软件蓝图的可视化建模语言.支持面向对象系统的分析.设计.实现和交付等各个环节,可以用于系统的理解.设计.浏览.维护和信息控制.UML是由基本构造块.规则和其中的公共机制组成. UML的基本构造块包括事物.关系和图.事物是构成模型图的一些基本图示标识,标识一些面向对象的基本概念.关系是基本图示符号之间的关系.图是从特定的视角对系统所做的抽象描述. 最让人头疼的就是四种基本的关系:关联.依赖.泛化.实现.如图,关联还有特殊情况:聚合和组合. 一.下面让我们先从理论上来理解

Java基础总结(内部版)【转】

一.JVM 1.内存模型 1.1.1 内存分几部分 (1)程序计数器 可看作当前线程所执行的字节码的**行号指示器**.字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 在线程创建时创建.执行本地方法时,PC的值为null.为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,线程私有. (2)Java虚拟机栈 线程私有,生命周期同线程.**每个方法在执行同时,创建栈帧*

02-NSArray、NSSet、NSDictionary 集合类

NSArray.NSSet.NSDictionary 集合类 可以用来装东西 OC数组只能存放OC对象.不能存放非OC对象类型,如int.struct\enu等 OC数组不能存放nil值 1.NSArray   不可变数组 1>.  NSArray *array1 = [NSArray array]; // 由于是不可变数组,永远都是空数组 2>.  NSArray *array2 = [NSArray arrayWithObject:@"whblap"]; 3>. 

写给大家看的设计书(第3版)PDF下载高清完整扫描原版

这本书出自一位世界级设计师之手.复杂的设计原理在书中凝炼为亲密性.对齐.重复和对比4 个基本原则.作者以其简洁明快的风格,将优秀设计所必须遵循的这4 个基本原则及其背后的原理通俗易懂地展现在读者面前.本书包含大量的示例,让你了解怎样才能按照自己的方式设计出美观且内容丰富的产品. 此书适用于各行各业需要从事设计工作的读者,也适用于有经验的设计人员.需要学习的朋友可以通过网盘下载pdf版 http://putpan.com/fs/8y1i5bce5n5s1h8u0/ 作者简介 Robin Willi

v-for为什么要加key,能用index作为key么

前言 在vue中使用v-for时,一直有几个疑问: v-for为什么要加key 为什么有时候用index作为key会出错 带着这个疑问,结合各种博客和源码,终于有了点眉目. virtual dom 要理解diff的过程,先要对virtual dom有个了解,这里简单介绍下. [作用] 我们都知道重绘和回流,回流会导致dom重新渲染,比较耗性能:而virtual dom就是用一个对象去代替dom对象,当有多次更新dom的动作时,不会立即更新dom,而是将变化保存到一个对象中,最终一次性将改变渲染出

初学设计模式之桥接模式

初学设计模式之桥接模式 1.什么是桥接模式 官方解答:将抽象部分与实现部分分离,使它们都独立的变化 2.先感性的认识一下桥接模式 一看这么官方的解释,这车速,有点晕车,没关系,开车之前前咱们先骑自行车感受一下慢速行驶. 假如有一道常识题希望答题人输出的正确 答案为:居里夫人是化学家(干扰因素有雨果.生物学家) 选择题:A  居里夫人是化学家 B  居里夫人是生物学家. C  雨果是化学家 D  雨果是生物学家 连线题:(人名)         是            (头衔) 居里夫人