图算法小结(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;
}

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。