poj2457 Part Acquisition(dijkstra||spfa+路径记录优化)

来源:互联网 发布:2009年网络歌曲大全 编辑:程序博客网 时间:2024/06/06 07:03


http://poj.org/problem?id=2457

题意:m个关系n个货物,关系中的a b代表可以拿货物a换取货物b,构造一条最短路,起点1终点n,输出交换次数和路径。


思路:看了这个教程。注意是有向图。dijkstra和spfa事实上是一个思路,都是找到最短路后,沿着这条路根据找路时存储的前驱来存储相应的节点。记录路径的方式也有几种,刚开始用数组记录,绕的我头都晕了。后来用栈优化,一目了然。


这题后来也回顾过,好好想了想初始化问题。dijkstra和spfa其实都可以一开始就将dis数组置为无穷大,pre数组更新为-1。dijkstra中仅仅把dis[1]置为0,但是未访问,这样所有的点都能算上。spfa中第一个点要入队列,dis[1]置为0,变成已访问,同样可以算上所有的点。

这样初始化就不会对起点进行一些对pre和dis的繁琐操作,使思路更加清晰。


首先来终极简化版本,以后做路径题方式就是以这个为参考:

#include <stdio.h>#include <algorithm>#include <stdlib.h>#include <string.h>#include <iostream>#include <queue>#include <stack>using namespace std;typedef long long LL;const int N = 1005;const int INF = 0x3f3f3f3f;int dis[N], G[N][N], pre[N], ans[N], n;bool vis[N];void dijkstra(int s){    memset(vis, false, sizeof(vis));    memset(pre, -1, sizeof(pre));    for(int i = 1; i <= n; i++)        dis[i] = INF;    dis[s] = 0;    for(int i = 1; i <= n; i++)    {        int k = -1;        for(int j = 1; j <= n; j++)        {            if(!vis[j] && (k==-1 || dis[j]<dis[k]))                k = j;        }        if(k == -1) break;//已经遍历所有的点        vis[k] = true;        for(int j = 1; j <= n; j++)        {            if(!vis[j] && dis[k]+G[k][j]<dis[j])            {                dis[j] = dis[k]+G[k][j];                pre[j] = k;            }        }    }}void spfa(int s){    memset(vis, false, sizeof(vis));    memset(pre, -1, sizeof(pre));    queue<int>que;    for(int i = 1; i <= n; i++)        dis[i] = INF;    dis[s] = 0;    vis[s] = true;    que.push(s);    while(!que.empty())    {        int now = que.front();        que.pop();        vis[now] = false;        for(int i = 1; i <= n; i++)        {            if(dis[now]+G[now][i]<dis[i])            {                dis[i] = dis[now]+G[now][i];                pre[i] = now;                if(!vis[i])                {                    vis[i] = true;                    que.push(i);                }            }        }    }}void Print(){    stack<int>path;    int now = n, cnt = 0;    if(dis[n] == INF) printf("-1\n");    else    {        while(now != -1)        {            path.push(now);            now = pre[now];            cnt++;        }        printf("%d\n", cnt);        while(!path.empty())        {            printf("%d\n", path.top());            path.pop();        }    }}int main(){  //  freopen("in.txt", "r", stdin);    int m, s, e;    scanf("%d%d", &m, &n);    for(int i = 1; i <= n; i++)        for(int j = 1; j <= n; j++)        {            if(i == j) G[i][j] = 0;            else G[i][j] = INF;        }    for(int i = 1; i <= m; i++)    {        scanf("%d%d", &s, &e);        G[s][e] = 1;    }  //  dijkstra(1);    spfa(1);    Print();    return 0;}


然后是这个把我绕晕了的数组记录路径:


#include <stdio.h>#include <algorithm>#include <stdlib.h>#include <string.h>#include <iostream>#include <queue>#include <stack>using namespace std;typedef long long LL;const int N = 1005;const int INF = 0x3f3f3f3f;int dis[N], G[N][N], pre[N], ans[N], n;bool vis[N];void dijkstra(int s){    memset(vis, false, sizeof(vis));    memset(pre, -1, sizeof(pre));    for(int i = 1; i <= n; i++)    {        dis[i] = G[s][i];        if(dis[i]!=INF && i!=s) pre[i] = s;    }    dis[s] = 0;    vis[s] = true;    for(int i = 1; i <= n; i++)    {        int k = -1;        for(int j = 1; j <= n; j++)        {            if(!vis[j] && (k==-1 || dis[j]<dis[k]))                k = j;        }        if(k == -1) break;//已经遍历所有的点        vis[k] = true;        for(int j = 1; j <= n; j++)        {            if(!vis[j] && dis[k]+G[k][j]<dis[j])            {                dis[j] = dis[k]+G[k][j];                pre[j] = k;            }        }    }}void spfa(int s){    memset(vis, 0, sizeof(vis));    memset(pre, -1, sizeof(pre));    queue<int>que;    for(int i = 1; i <= n; i++)        dis[i] = INF;//初值记得赋无穷,而不是G数组中的值!    dis[s] = 0;    vis[s] = true;    que.push(s);    while(!que.empty())    {        int now = que.front();        que.pop();        vis[now] = false;//记得还原!!        for(int i = 1; i <= n; i++)        {            if(dis[now]+G[now][i]<dis[i])            {                dis[i] = dis[now]+G[now][i];                pre[i] = now;                if(!vis[i])                {                    vis[i] = true;                    que.push(i);                }            }        }    }}void print(){    memset(ans, 0, sizeof(ans));    int cnt = 0;    if(dis[n] == INF)        printf("-1\n");    else    {        ans[cnt++] = n;        int tmp = pre[n];        while(tmp != -1)        {            ans[cnt++] = tmp;            tmp = pre[tmp];        }        printf("%d\n", cnt);        for(int i = cnt-1; i >= 0; i--)            printf("%d\n", ans[i]);     //   printf("sad");    }}int main(){   // freopen("in.txt", "r", stdin);    int m, s, e;    scanf("%d%d", &m, &n);    for(int i = 1; i <= n; i++)        for(int j = 1; j <= n; j++)        {            if(i == j) G[i][j] = 0;            else G[i][j] = INF;        }    for(int i = 1; i <= m; i++)    {        scanf("%d%d", &s, &e);        G[s][e] = 1;    }//    dijkstra(1);    spfa(1);    print();    return 0;}



后来用两种floyd敲了两次,都是TLE。大体思路就是二维数组在DP的时候已经将路径的前驱保存了下来,我们正好利用了这种特殊的性质。记录前驱就和前面思想一样,记录后继很方便,就是floyd的特权吧,然而复杂度太高,人家不让过哎= =


floyd记录前驱的路径记录法:

#include <stdio.h>#include <algorithm>#include <stdlib.h>#include <string.h>#include <iostream>#include <queue>using namespace std;typedef long long LL;const int N = 1005;const int INF = 0x3f3f3f3f;int G[N][N], pre[N][N], ans[N], n;void floyd(){    for(int k = 1; k <= n; k++)        for(int i = 1; i <= n; i++)            for(int j = 1; j <= n; j++)            {                if(G[i][k]+G[k][j] < G[i][j])                {                    G[i][j] = G[i][k]+G[k][j];                    pre[i][j] = pre[k][j];                }            }    pre[1][1] = -1;}void print(){    memset(ans, 0, sizeof(ans));    int cnt = 0;    if(G[1][n] == INF)        printf("-1\n");    else    {        ans[cnt++] = n;        int tmp = pre[1][n];        while(tmp != -1)        {            ans[cnt++] = tmp;            tmp = pre[1][tmp];        }        printf("%d\n", cnt);        for(int i = cnt-1; i >= 0; i--)            printf("%d\n", ans[i]);    }}int main(){  //  freopen("in.txt", "r", stdin);    int m, s, e;    scanf("%d%d", &m, &n);    for(int i = 1; i <= n; i++)        for(int j = 1; j <= n; j++)        {            if(i == j) G[i][j] = 0;            else G[i][j] = INF;            pre[i][j] = i;        }    for(int i = 1; i <= m; i++)    {        scanf("%d%d", &s, &e);        G[s][e] = 1;    }    floyd();    print();    return 0;}




floyd记录后继的路径记录法:

#include <stdio.h>#include <algorithm>#include <stdlib.h>#include <string.h>#include <iostream>#include <queue>using namespace std;typedef long long LL;const int N = 1005;const int INF = 0x3f3f3f3f;int G[N][N], pre[N][N], n;void floyd(){    for(int k = 1; k <= n; k++)        for(int i = 1; i <= n; i++)            for(int j = 1; j <= n; j++)            {                if(G[i][k]+G[k][j] < G[i][j])                {                    G[i][j] = G[i][k]+G[k][j];                    pre[i][j] = pre[i][k];                }            }    pre[n][n] = -1;}void print(){    int cnt = 0;    if(G[1][n] == INF)        printf("-1\n");    else    {        printf("%d\n", G[1][n]+1);        int tmp = pre[1][n];        printf("%d\n", 1);        while(tmp != -1)        {            printf("%d\n", tmp);            tmp = pre[tmp][n];        }    }}int main(){  //  freopen("in.txt", "r", stdin);    int m, s, e;    scanf("%d%d", &m, &n);    for(int i = 1; i <= n; i++)        for(int j = 1; j <= n; j++)        {            if(i == j) G[i][j] = 0;            else G[i][j] = INF;            pre[i][j] = j;        }    for(int i = 1; i <= m; i++)    {        scanf("%d%d", &s, &e);        G[s][e] = 1;    }    floyd();    print();    return 0;}


0 0
原创粉丝点击