avatar
fireworks99
keep hungry keep foolish

POJ 3255 Roadblocks and 3463 Sightseeing(次短路)

POJ 3255 Roadblocks

N个点,M条双向边,求1到N的严格次短路

Idea 1

1到n的次短路长度必然产生于:从1走到x的最短路 + edge[x][y] + y到n的最短路

首先预处理好1到每一个节点的最短路,和n到每一个节点的最短路

然后枚举每一条边作为中间边(x,y)或者(y,x),如果加起来长度等于最短路长度则跳过,否则更新从1走到x的最短路 + edge[x][y] + y到n的最短路 给dist[n] 比较 找大于dist[n] 且是最小的那一个

spfa(1) + spfa(n)

原文链接: https://blog.csdn.net/huangshuai147/article/details/69105576

Code

#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int INF = 0x3f3f3f3f;

int n, m;

int cnt;
struct edge
{
    int to, w, pre;
} a[200005];

bool vis[5005];///表示是否在队列里
int head[5005], frdis[5005], redis[5005], tot, sum;

void add(int from, int to, int w)
{
    a[cnt].to = to;
    a[cnt].w = w;
    a[cnt].pre = head[from];
    head[from] = cnt;
    cnt++;
}

void init()
{
    cnt = 0;
    for(int i = 0; i <= n; ++i)
    {
        frdis[i] = INF;
        redis[i] = INF;
        head[i] = -1;
        vis[i] = 0;
    }
}

void spfa(int start, int * dis)
{
    memset(vis, 0, sizeof(vis));
    deque<int> q;
    dis[start] = 0;
    q.push_front(start);
    vis[start] = 1;///表示在队列里
    tot = 1, sum = 0;
    while(q.size())
    {
        int first = q.front();
        q.pop_front();
        vis[first] = 0;
        tot--;
        sum -= dis[first];

///同dijkstra,以first为中转点,而非a[i].from
        for(int i = head[first]; ~i; i = a[i].pre)
        {
            int t = a[i].to;
            if(dis[t] > dis[first] + a[i].w)
            {
                dis[t] = dis[first] + a[i].w;
                if(!vis[t])
                {
                    vis[t] = 1;
                    if(q.empty() || dis[t] > dis[q.front()] || dis[t] * tot >= sum)
                        q.push_back(t);
                    else
                        q.push_front(t);
                    sum += dis[t];
                    tot++;
                }
            }
        }
    }
    return ;
}

int main()
{
    scanf("%d%d", &n, &m);
    init();
    int u, v, w;
    for(int i = 0; i < m; ++i)
    {
        scanf("%d%d%d", &u, &v, &w);
        add(u, v, w);
        add(v, u, w);
    }
    spfa(1, frdis);
    spfa(n, redis);
    int ans = INF;
    for(int i = 1; i <= n; ++i)
        for(int j = head[i]; ~j; j = a[j].pre)
        {
            v = a[j].to;
            w = a[j].w;
            int tem = frdis[i] + w + redis[v];
            if(tem > frdis[n] && tem < ans)
                ans = tem;
        }
    cout << ans << '\n';
    return 0;
}

POJ 3463 Sightseeing

N个点,M条单向边,求最短路数目+比最短路长单位1的次短路条数

Idea 2

(s,u,v)代表从s——v的最短路,u是中间一个必经点

最短路和次短路的比较:

(s,u,v)最短路=到达前一个点u的最短路+(u,v)的最短路。

因而最短路求解有两种情况:

  1. (s,u,v)次短路=到达前一个点u的次短路+(u,v)的最短路
  2. (s,u,v)次短路=到达前一个点u的最短路+(u,v)的次短路。

用:d[v][0]代表最短路,d[v][1]代表次短路。

(一)如果从s—u的最短路加上(u,v)权值的距离小于原来的d[v][0] ,我们就可把次短路d[v][1] 的值更新为d[v][0],就该题而言,此时可以把次短路的条数也更新为这个点的最短路的条数;把当前最短路d[v][0]的值更新成最小的,和原来最短路的松弛操作是一样的。

(二)如果从s—u的最短路加上(u,v)权值的距离大于原来的d[v][0],这就说明这条路路径就可能是一条次短路,那我们需要判断这条路的值dist,和原来的次短路的值d[v][1]进行比较!

  1. 如果它比原来的次短路要大,直接跳过.
  2. 如果它比原来的次短路要小,那么我们就需要更新最短路的长度 .
  3. 如果与原来的次短路相等,说明我们的次短路有另一种(这里并不一定是只有1种)方法可以到达终点v。这里要加的种类是:原来到v点的次短路的条数+到u点的次短路(或者最短路,(u,v)并不知道是不是一定是最短路,如果是次短路,那么此时加的就是到u的最短路)的条数。

原文链接: https://blog.csdn.net/ydd97/article/details/47919551

Code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 10005;

struct edge
{
    int to, w, pre;
}a[N << 1];

int n, m, cnt, head[N], d[N][2], c[N][2], vis[N][2];

void add(int from, int to, int w)
{
    a[cnt].to = to;
    a[cnt].w = w;
    a[cnt].pre = head[from];
    head[from] = cnt++;
}

void Dijkstra(int s)
{
    memset(d, INF, sizeof(d));
    memset(c, 0, sizeof(c));
    memset(vis, 0, sizeof(vis));
    d[s][0] = 0;
    c[s][0] = 1;

    int pos = s, idx;
    while(1)
    {
        pos = -1;
        for(int i = 1; i <= n; ++i)
            for(int j = 0; j < 2; ++j)
                if(!vis[i][j] && (pos == -1 || d[i][j] < d[pos][idx]))
                    pos = i, idx = j;
        if(pos == -1)
            break;

        vis[pos][idx] = 1;
        for(int i = head[pos]; ~i; i = a[i].pre)
        {
            int to = a[i].to;
            int dis = d[pos][idx] + a[i].w;

            if(dis < d[to][0])///This path is shorter than the shortest path.
            {
                d[to][1] = d[to][0], c[to][1] = c[to][0];
                d[to][0] = dis, c[to][0] = c[pos][idx];
            }
            else if(dis == d[to][0])
                c[to][0] += c[pos][idx];
            else if(dis < d[to][1])///longer than the shortest but shorter than the second shortest
                d[to][1] = dis, c[to][1] = c[pos][idx];
            else if(dis == d[to][1])
                c[to][1] += c[pos][idx];
        }
    }
}

int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        cnt = 0;
        memset(head, -1, sizeof(head));
        scanf("%d%d", &n, &m);
        int u, v, w;
        for(int i = 0; i < m; ++i)
        {
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w);
        }

        int start, over;
        scanf("%d%d", &start, &over);
        Dijkstra(start);
        int ans = c[over][0];
        if(d[over][1] == d[over][0] + 1)///be careful which is smaller
            ans += c[over][1];
        cout << ans << '\n';
    }
    return 0;
}

POJ 3255 of Idea 2

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int M = 100005;
const int N = 5005;
struct edge
{
    int to, w, pre;
} a[M << 1];

int n, m, cnt, head[N], d[N][2], vis[N][2];

void add(int from, int to, int w)
{
    a[cnt].to = to;
    a[cnt].w = w;
    a[cnt].pre = head[from];
    head[from] = cnt++;
}

void Dijkstra(int s)
{
    memset(d, INF, sizeof(d));
    memset(vis, 0, sizeof(vis));
    d[s][0] = 0;

    int pos = s, idx;
    while(1)
    {
        pos = -1;
        for(int i = 1; i <= n; ++i)
            for(int j = 0; j < 2; ++j)
                if(!vis[i][j] && (pos == -1 || d[i][j] < d[pos][idx]))
                    pos = i, idx = j;
        if(pos == -1)
            break;

        vis[pos][idx] = 1;
        for(int i = head[pos]; ~i; i = a[i].pre)
        {
            int to = a[i].to;
            int dis = d[pos][idx] + a[i].w;

            if(dis < d[to][0])///This path is shorter than the shortest path.
                d[to][1] = d[to][0], d[to][0] = dis;
            else if(dis < d[to][1])///longer than the shortest but shorter than the second shortest
                d[to][1] = dis;
        }
    }
}

int main()
{
    cnt = 0;
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &m);
    int u, v, w;
    for(int i = 0; i < m; ++i)
    {
        scanf("%d%d%d", &u, &v, &w);
        add(u, v, w);
        add(v, u, w);
    }

    Dijkstra(1);
    cout << d[n][1] << '\n';
    return 0;
}
Site by Baole Zhao | Powered by Hexo | theme PreciousJoy