Shortest record

Introduction

This is used to record my brushing records and study notes for the exercises in the book "Informatics Olympiad One Pass · Improvement".

It is generally written in special topics (it may take a long time to load all together ...), for example, this chapter is used to record the shortest path .

Let me insert one more sentence: Loj is really a good OJ. If Luogu is the best OIer community, then Loj is the best question area.

PS: The "best" here only refers to the exercises that bring me the best feeling, such as good style of painting, and only represents personal opinions.

Special presentation : The shortest route, a pivotal piece of content in graph theory, many algorithms need to be used flexibly, and the variety of topics is always inseparable from the core idea.

If you don't know any of Dijksral, SPFA, Floyd, you can check out my article .

first question

# 10072 "One Bentong 3.2 Example 1" Sightseeing Trip

Am I too weak? Floyd, who has studied for so many years, didn't even know that it has this function ....

To find the smallest ring of an undirected graph, Floyd is preferred.

Here mainly talk about the specific principle of using Floyd to solve

To use Floyd to solve the smallest cycle problem of undirected graphs, we must first understand the significance of each step of Floyd.

Although the four-layer code is very good

for(int k=1;k<=n;k++){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);

But what happened in this process, why can it be used to find the smallest loop?

First understand a truth:

Consider the smallest ring \ (u \ to v \ to k \ to u \) in a graph, if we arbitrarily remove one of the edges \ (u \ to v \) ,

Then the remaining \ (v \ to k \ to u \) must be the shortest path between \ (u \ to v \) in the figure .

So how does this relate to Floyd's algorithm? There is another reason:

When the Floyd algorithm enumerates \ (k_i \) , the shortest path of the first \ (k-1 \) points has been obtained ,

These \ (k-1 \) points do not include the point \ (k \) , and their shortest path also does not include the \ (k \) point.

Then we can select two points \ (i, j \) from the first \ (k-1 \) points , because \ (i \ to j \) is already the shortest path between $ i, j $, and this The path does not contain \ (k \) points.

So connecting \ (i \ to j \ to k \ to i \) , we get a minimum cycle through \ (i, j, k \)

Finally, update the minimum value of \ (ans \) each time .

The explanation of this question is reproduced from this blog .

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cmath>
#include <vector>
#define N 310
#define maxd 0x3f3f3f3f
using namespace std;

int n, m, a[N][N], d[N][N], pre[N][N];
long long ans = maxd;
vector<int> path;

int read() {
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
    while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    return x * f;
}

void get_path(int x, int y) {
    if (!pre[x][y])
        return;
    get_path(x, pre[x][y]);
    path.push_back(pre[x][y]);
    get_path(pre[x][y], y);
    return;
}

int main() {
    n = read();
    m = read();
    int x, y, z;
    memset(a, 0x3f, sizeof(a));
    for (int i = 1; i <= n; i++) a[i][i] = 0;
    for (int i = 1; i <= m; i++) {
        x = read();
        y = read();
        z = read();
        a[x][y] = a[y][x] = min(a[x][y], z);
    }
    memcpy(d, a, sizeof(a));
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i < k; i++)
            for (int j = i + 1; j < k; j++)
                if ((long long)d[i][j] + a[j][k] + a[k][i] < ans) {
                    ans = d[i][j] + a[j][k] + a[k][i];
                    path.clear();
                    path.push_back(i);
                    get_path(i, j);
                    path.push_back(j);
                    path.push_back(k);
                }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (d[i][j] > d[i][k] + d[k][j]) {
                    d[i][j] = d[i][k] + d[k][j];
                    pre[i][j] = k;
                }
    }
    if (ans == maxd) {
        printf("No solution.\n");
        return 0;
    }
    for (int i = 0; i < path.size(); i++) printf("%d ", path[i]);
    return 0;
}

The second question

# 10073 "One case of 3.2 cases 2" to save soldier Ryan

I have used BFS + to solve this problem, because this method saves space than the shortest path, and the code given below is also my method.

But this does not affect the idea of explaining the shortest path of the layered graph here .

The more direct idea is to divide the graph into \ (2 ^ k \) layers ( \ (k \) is the number of keys), and each layer indicates the state of having the key. (Not just pressure)

Then just mess around with the edges to get an extremely complex figure, and run the shortest path on it.

Note: The answer is \ (max_ {1 \ leq i \ leq 2 ^ k} \ {dis [i] [n] \} \) . Below is the code for BFS + pressure.

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstring>
#include <queue>
#define N 15
using namespace std;

int n, m, p, k, s;
int door[N][N][N][N], key[N][N][N], dis[N][N][1 << 14], cnt[N][N];
bool vis[N][N][1 << 14];
struct node {
    int x, y, val;
};
int dx[5] = { 0, 0, 0, 1, -1 };
int dy[5] = { 0, 1, -1, 0, 0 };
queue<node> q;

int read() {
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
    while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    return x * f;
}

bool chck(int x, int y, int fx, int fy, int val, int nxt) {
    if (fx < 1 || fx > n || fy < 1 || fy > m)
        return false;
    int d = door[x][y][fx][fy];
    if (d == -1)
        return false;
    if (d && !(val & (1 << d)))
        return false;
    if (vis[fx][fy][nxt])
        return false;
    return true;
}

int bfs() {
    memset(vis, false, sizeof(vis));
    int first = 0;
    for (int i = 1; i <= cnt[1][1]; i++) first |= (1 << key[1][1][i]);
    q.push((node){ 1, 1, first });
    while (!q.empty()) {
        int x = q.front().x;
        int y = q.front().y;
        int val = q.front().val;
        q.pop();
        if (x == n && y == m)
            return dis[x][y][val];
        for (int i = 1; i <= 4; i++) {
            int fx = dx[i] + x;
            int fy = dy[i] + y;
            int nxt = val;
            for (int i = 1; i <= cnt[fx][fy]; i++) nxt |= (1 << key[fx][fy][i]);
            if (!chck(x, y, fx, fy, val, nxt))
                continue;
            vis[fx][fy][nxt] = true;
            dis[fx][fy][nxt] = dis[x][y][val] + 1;
            q.push((node){ fx, fy, nxt });
        }
    }
    return -1;
}

int main() {
    n = read();
    m = read();
    p = read();
    k = read();
    int type, x, y, xx, yy;
    for (int i = 1; i <= k; i++) {
        x = read();
        y = read();
        xx = read();
        yy = read();
        type = read();
        if (type)
            door[x][y][xx][yy] = door[xx][yy][x][y] = type;
        else
            door[x][y][xx][yy] = door[xx][yy][x][y] = -1;
    }
    s = read();
    for (int i = 1; i <= s; i++) {
        x = read();
        y = read();
        type = read();
        key[x][y][++cnt[x][y]] = type;
    }
    printf("%d\n", bfs());
    return 0;
}

The third question

# 10074 "One case of 3.2 cases 3" set up a telephone line

See this article specifically .

Fourth question

# 10075 "One book pass 3.2 practice 1" farm party

I took the original question in the previous exam ..., and then I did not notice that it was a one-way edge, I directly output \ (max \ {dis [i] \ times 2 \} \) , and then burst 0 to roll thick ....

Then after the exam, I thought of a very violent method:

It is to use each node as the root node to run the single-source shortest path, and then the answer is \ (max \ {dis [1, i] + dis [i, 1] \} \) .

Seems to run very fast

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <queue>
#define N 1010
#define M 100010
using namespace std;

int n, m, v, dis[N], diss[N], head[N], cnt = 0;
bool vis[N];
struct Edge {
    int nxt, to, val;
} edge[M];
priority_queue<pair<int, int> > q;

int read() {
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
    while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    return x * f;
}

void addedge(int x, int y, int z) {
    cnt++;
    edge[cnt].nxt = head[x];
    edge[cnt].to = y;
    edge[cnt].val = z;
    head[x] = cnt;
    return;
}

void dij(int s) {
    memset(vis, false, sizeof(vis));
    memset(dis, 0x3f, sizeof(dis));
    dis[s] = 0;
    q.push(make_pair(0, s));
    while (!q.empty()) {
        int now = q.top().second;
        q.pop();
        if (vis[now])
            continue;
        vis[now] = true;
        for (int i = head[now]; i; i = edge[i].nxt) {
            int y = edge[i].to, z = edge[i].val;
            if (dis[y] > dis[now] + z) {
                dis[y] = dis[now] + z;
                q.push(make_pair(-dis[y], y));
            }
        }
    }
    return;
}

int main() {
    n = read(), m = read(), v = read();
    int x, y, z;
    for (int i = 1; i <= m; i++) {
        x = read(), y = read(), z = read();
        addedge(x, y, z);
    }
    dij(v);
    memcpy(diss, dis, sizeof(dis));
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        dij(i);
        ans = max(ans, dis[v] + diss[i]);
    }
    printf("%d\n", ans);
    return 0;
}

Of course, this is not a positive solution. (nonsense)

In a word: it's enough to run the shortest path twice on both sides. (Implemented here with simple SPFA)

#include <bits/stdc++.h>
using namespace std;
int n, m, k, ans;
int ecnt = 0, tcnt = 0;
int zhead[1000 + 5], fhead[1000 + 5];
int zdis[1000 + 5], fdis[1000 + 5];
bool vst[1000 + 5];
struct edge {
    int from, to, len;
    int nxt;
} g[100000 + 5], f[100000 + 5];
inline int read() {
    int x = 0;
    char ch = getchar();
    while (ch < 48 || ch > 57) ch = getchar();
    while (ch <= 57 && ch >= 48) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
    return x;
}
inline void addedge1(int u, int v, int w) {
    ecnt++;
    g[ecnt].from = u;
    g[ecnt].to = v;
    g[ecnt].len = w;
    g[ecnt].nxt = zhead[u];
    zhead[u] = ecnt;
}
inline void addedge2(int u, int v, int w) {
    tcnt++;
    f[ecnt].from = u;
    f[ecnt].to = v;
    f[ecnt].len = w;
    f[ecnt].nxt = fhead[u];
    fhead[u] = tcnt;
}
inline void SPFA1(int S) {
    memset(fdis, 0x3f, sizeof(fdis));
    memset(vst, 0, sizeof(vst));
    vst[S] = 1;
    queue<int> q;
    fdis[S] = 0;
    q.push(S);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vst[u] = 0;
        for (register int i = fhead[u]; i; i = f[i].nxt) {
            int v = f[i].to;
            if (fdis[v] > fdis[u] + f[i].len) {
                fdis[v] = fdis[u] + f[i].len;
                if (!vst[v]) {
                    vst[v] = 1;
                    q.push(v);
                }
            }
        }
    }
}
inline void SPFA2(int S) {
    memset(zdis, 0x3f, sizeof(zdis));
    memset(vst, 0, sizeof(vst));
    vst[S] = 1;
    queue<int> q;
    zdis[S] = 0;
    q.push(S);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vst[u] = 0;
        for (register int i = zhead[u]; i; i = g[i].nxt) {
            int v = g[i].to;
            if (zdis[v] > zdis[u] + g[i].len) {
                zdis[v] = zdis[u] + g[i].len;
                if (!vst[v]) {
                    vst[v] = 1;
                    q.push(v);
                }
            }
        }
    }
}
int main() {
    n = read(), m = read(), k = read();
    for (register int i = 1, x, y, z; i <= m; i++) {
        x = read(), y = read(), z = read();
        addedge1(x, y, z);
        addedge2(y, x, z);
    }
    SPFA1(k);
    SPFA2(k);
    for (register int i = 1; i <= n; i++) ans = max(ans, zdis[i] + fdis[i]);
    printf("%d\n", ans);
    system("pause");
    return 0;
}

Question 5

# 10076 "One pass 3.2 Exercise 2" Roadblocks

There are three methods for seeking strict secondary short circuits:

  1. Dijksral records two values ​​again.
  2. Dijkscal + A * again.
  3. Dijkscal twice.

The following content is reproduced from this article .

dijkstra:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cmath>
#include <queue>
#define N 5010
#define M 100010
using namespace std;

int n, m, dis[N], diss[N], head[N], cnt = 0;
struct Edge {
    int nxt, to, val;
} ed[M << 1];
priority_queue<pair<int, int> > q;

int read() {
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
    while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    return x * f;
}

void addedge(int x, int y, int z) {
    ++cnt;
    ed[cnt].nxt = head[x];
    ed[cnt].to = y;
    ed[cnt].val = z;
    head[x] = cnt;
    return;
}

void dij() {
    memset(dis, 0x3f, sizeof(dis));
    memset(diss, 0x3f, sizeof(diss));
    dis[1] = 0;
    q.push(make_pair(0, 1));
    while (!q.empty()) {
        int d = -q.top().first, now = q.top().second;
        q.pop();
        if (d > diss[now])
            continue;
        for (int i = head[now]; i; i = ed[i].nxt) {
            int y = ed[i].to, z = ed[i].val;
            int d2 = d + z;
            if (dis[y] > d2) {
                swap(dis[y], d2);
                q.push(make_pair(-dis[y], y));
            }
            if (diss[y] > d2 && dis[y] < d2) {
                diss[y] = d2;
                q.push(make_pair(-diss[y], y));
            }
        }
    }
    return;
}

int main() {
    n = read(), m = read();
    int x, y, z;
    for (int i = 1; i <= m; i++) {
        x = read(), y = read(), z = read();
        addedge(x, y, z);
        addedge(y, x, z);
    }
    dij();
    printf("%d\n", diss[n]);
    return 0;
}

dijkstra+A*:

First use dijkstra to process the shortest distance from n to each point, and then call the A * algorithm.

Start with 1 and add the edge weights of adjacent points each time, and then store them in the queue, so that each time you store the distance from 1 to each point.

Then sort according to this distance + the shortest distance from the current point to n.

Then the first time n points are taken out is the shortest path from 1 to n, and then continue to add adjacent edge weights to the queue.

Then the second processing to n is the shortest path plus the value of a shortest side other than the side through which the shortest path passes, which is the second shortest distance.

#include <cstring>
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
 
#define INF 0x3f3f3f3f
 
using namespace std;
 
const int maxn=5000+10;
int n,m,u,v,val;
int dis[maxn];
struct P
{
    int to,cost;
    bool operator < (const P & a) const
    {
        return cost>a.cost;
    }
};
 
vector<P> edge[maxn];
 
void Dijkstra()
{
    fill(dis,dis+n+2,INF);
    dis[n]=0;
    priority_queue<P> qu;
    qu.push(P{n,0});
    while(!qu.empty())
    {
        P x=qu.top();
        qu.pop();
        for(int i=0;i<edge[x.to].size();i++)
        {
            P y=edge[x.to][i];
            if(dis[y.to]>dis[x.to]+y.cost)
            {
                dis[y.to]=dis[x.to]+y.cost;
                qu.push(P{y.to,dis[y.to]});
            }
        }
    }
}
 
struct node
{
    int to,len;
    bool operator < (const node & a) const
    {
        return len+dis[to]>a.len+dis[a.to];
    }
};
 
int A_star()
{//if(dis[1]==INF) return -1;当不存在最短路时要加上,不然会死循环。
    priority_queue<node> qu;
    qu.push(node{1,0});
    int num=0;
    while(!qu.empty())//这个地方有点搜索的味道,从1开始不断加上相邻的点的边权,然后放入队列。就是从1开始不断向n拓展
    {
        node a=qu.top();
        qu.pop();
        if(a.to==n) num++;
        if(num==2) return a.len;
        for(int i=0;i<edge[a.to].size();i++)
        {
            P b=edge[a.to][i];
            qu.push(node{b.to,a.len+b.cost});//到b.to这个点,然后1到b.to的距离就是a.to的距离加上ab间边的权值。
        }
    }
    return -1;
}
 
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i<=n;i++)
            edge[i].clear();
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&val);
            edge[u].push_back(P{v,val});
            edge[v].push_back(P{u,val});
        }
        Dijkstra();
        printf("%d\n",A_star());
    }
    return 0;
}

Twice shortest:

Run the shortest path once from the starting point, then the shortest path again from the end point, and then traverse all the edges.

The distance of the secondary short circuit is the weight of one edge plus the shortest path from the starting point to one point plus the shortest path from the end point to another point

#include <cstring>
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#pragma GCC optimize(2)
#define INF 0x3f3f3f3f
 
using namespace std;
 
const int N=5000+5;
int n,m,u,v,val;
struct P
{
    int to,cost;
};
vector<vector<P> > G;//刚学的二维vector使用,使用前需要resize其大小
int dis1[N],dis2[N],vis[N];
 
void spfa(int s,int *dis)
{
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    queue<int> qu;
    qu.push(s);
    while(!qu.empty()){
        int U=qu.front();
        qu.pop();
        vis[U]=0;
        for(int i=0;i<G[U].size();i++){
            P V=G[U][i];
            if(dis[V.to]>dis[U]+V.cost){
                dis[V.to]=dis[U]+V.cost;
                if(!vis[V.to]){
                    vis[V.to]=1;
                    qu.push(V.to);
                }
            }
        }
    }
}
 
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF){
        G.resize(n+1); G.clear();
        memset(dis1,INF,sizeof(dis1));
        memset(dis2,INF,sizeof(dis2));
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&u,&v,&val);
            G[u].push_back(P{v,val});
            G[v].push_back(P{u,val});
        }
        spfa(1,dis1);
        spfa(n,dis2);
        int ans=INF;
        for(int i=1;i<=n;i++){//遍历所有的边
            for(int j=0;j<G[i].size();j++){
                P now=G[i][j];
                int temp=dis1[i]+dis2[now.to]+now.cost;
                if(temp>dis1[n] && temp<ans) ans=temp;//严格最小
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

Question 6

# 10077 "One book pass 3.2 Exercise 3" shortest path count

There is no right to undirected graphs. Is it okay to record the answer directly, so simple.

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>
#define MOD 100003
#define N 1000010
using namespace std;

int n, m, dep[N], ans[N];
bool vis[N];
vector<int> v[N];
queue<int> q;

int main() {
    scanf("%d %d", &n, &m);
    int x, y;
    for (int i = 1; i <= m; i++) {
        scanf("%d %d", &x, &y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    memset(vis, false, sizeof(vis));
    q.push(1);
    vis[1] = true;
    ans[1] = 1;
    while (!q.empty()) {
        int now = q.front();
        q.pop();
        for (int i = 0; i < v[now].size(); i++) {
            int to = v[now][i];
            if (!vis[to]) {
                vis[to] = true;
                dep[to] = dep[now] + 1;
                q.push(to);
            }
            if (dep[to] == dep[now] + 1)
                ans[to] = (ans[to] + ans[now]) % MOD;
        }
    }
    for (int i = 1; i <= n; i++) printf("%d\n", ans[i]);
    // system("pause");
    return 0;
}

Question 7

# 10078 "Yi Bentong 3.2 Practice 4" Happy New Year

a,b,c,d,eRun the shortest route from the source point and five nodes respectively , and record the number of DFS brute force enumeration schemes. (Total \ (2 ^ 5 \) )

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <queue>
#define N 50010
#define M 100010
using namespace std;

int n, m, v, dis[10][N], head[N], cnt = 0, a[10], ans = 999999999;
bool vis[N];
struct Edge {
    int nxt, to, val;
} edge[M << 1];
priority_queue<pair<int, int> > q;

int read() {
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
    while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    return x * f;
}

void addedge(int x, int y, int z) {
    cnt++;
    edge[cnt].nxt = head[x];
    edge[cnt].to = y;
    edge[cnt].val = z;
    head[x] = cnt;
    return;
}

void dij(int x, int s) {
    memset(vis, false, sizeof(vis));
    for (int i = 1; i <= n; i++) dis[x][i] = 0x3f3f3f3f;
    dis[x][s] = 0;
    q.push(make_pair(0, s));
    while (!q.empty()) {
        int now = q.top().second;
        q.pop();
        if (vis[now])
            continue;
        vis[now] = true;
        for (int i = head[now]; i; i = edge[i].nxt) {
            int y = edge[i].to, z = edge[i].val;
            if (dis[x][y] > dis[x][now] + z) {
                dis[x][y] = dis[x][now] + z;
                q.push(make_pair(-dis[x][y], y));
            }
        }
    }
    return;
}

int path[10], use[N];

void work() {
    int sum = dis[0][a[path[1]]];
    for (int i = 2; i <= 5; i++) sum += dis[path[i - 1]][a[path[i]]];
    ans = min(ans, sum);
    return;
}

void dfs(int x) {
    if (x == 6) {
        work();
        return;
    }
    for (int i = 1; i <= 5; i++) {
        if (use[i])
            continue;
        use[i] = true;
        path[x] = i;
        dfs(x + 1);
        use[i] = false;
    }
}

int main() {
    n = read(), m = read();
    for (int i = 1; i <= 5; i++) a[i] = read();
    int x, y, z;
    for (int i = 1; i <= m; i++) {
        x = read(), y = read(), z = read();
        addedge(x, y, z);
        addedge(y, x, z);
    }
    dij(0, 1);
    for (int i = 1; i <= 5; i++) dij(i, a[i]);
    memset(use, false, sizeof(use));
    dfs(1);
    printf("%d\n", ans);
    return 0;
}

Question 8

# 10079 "One Book 3.2 Exercise 5" Best Trade

The classic layered diagram is the shortest.

Create a three-layer map:

  1. The ordinary layer is to save the graph according to the input data.
  2. The buying layer is to connect it with the corresponding vertex of the ordinary layer, and the edge weight is the opposite of the buying price.
  3. The selling layer is to connect it with the corresponding vertex of the ordinary layer, and the edge weight is the selling price.

Then I found that I have negative power, so I can run SPFA again.

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <vector>
#define maxn 100010
#define maxm 500010
#define maxd 999999999
using namespace std;

int n, m, a[maxn], dis[maxn * 3 + 1];
bool use[maxn * 3 + 1];
queue<int> q;
struct node {
    int to, val;
};
vector<node> v[maxn * 3 + 1];

void addedge(int x, int y) {
    v[x].push_back((node){ y, 0 });
    v[x + n].push_back((node){ y + n, 0 });
    v[x + 2 * n].push_back((node){ y + 2 * n, 0 });
    v[x].push_back((node){ y + n, -a[x] });
    v[x + n].push_back((node){ y + 2 * n, a[x] });
    return;  //核心代码
}

void spfa() {
    for (int i = 1; i <= n; i++) dis[i] = -maxd;
    memset(use, false, sizeof(use));
    q.push(1);
    use[1] = true;
    dis[1] = 0;

    while (!q.empty()) {
        int now = q.front();
        q.pop();
        use[now] = false;
        for (int i = 0; i < v[now].size(); i++) {
            int y = v[now][i].to;
            int z = v[now][i].val;
            if (dis[y] < dis[now] + z) {
                dis[y] = dis[now] + z;
                if (!use[y]) {
                    q.push(y);
                    use[y] = true;
                }
            }
        }
    }
    return;
}

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    int x, y, z;
    for (int i = 1; i <= m; i++) {
        scanf("%d %d %d", &x, &y, &z);
        addedge(x, y);
        if (z == 2)
            addedge(y, x);
    }
    v[n].push_back((node){ n * 3 + 1, 0 });
    v[n * 3].push_back((node){ n * 3 + 1, 0 });
    n = 3 * n + 1;
    spfa();
    printf("%d\n", dis[n]);
    // system("pause");
    return 0;
}

Question 9

# 10080 "Yi Bentong 3.2 Exercise 6" car refueling

It is also the shortest problem of the classic hierarchical graph.

The idea is to divide the graph into \ (k + 1 \) layers, the node edge weight between each layer is \ (1 \) , and the layer \ (i \) indicates that it can also run \ (k-i + 1 \) step.

If there is a gas station at a certain point, then the edge weight from the high level to the base level is \ (A_i \) , otherwise it is \ (A_i + C_i \) .

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue>
#include <cmath>
#define N 500010
#define M 5000100
#define maxd 999999999
using namespace std;

int n, k, a, b, c;
int dis[N], oil;
bool use[N];
int head[N], cnt = 0;
struct node {
    int next, to, val;
} edge[M];
priority_queue<pair<int, int> > q;

void addedge(int x, int y, int z, int xx, int yy, int zz, int w) {
    int p1 = (z - 1) * n * n + (x - 1) * n + y;
    int p2 = (zz - 1) * n * n + (xx - 1) * n + yy;
    cnt++;
    edge[cnt].next = head[p1];
    edge[cnt].to = p2;
    edge[cnt].val = w;
    head[p1] = cnt;
    return;
}

void build() {
    scanf("%d %d %d %d %d", &n, &k, &a, &b, &c);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%d", &oil);
            for (int l = 1; l <= k; l++) {
                addedge(i, j, l, i, j, l + 1, 0);
            }
            if (oil) {
                for (int l = 2; l <= k + 1; l++) {
                    addedge(i, j, l, i, j, 1, a);
                }
                if (i < n)
                    addedge(i, j, 1, i + 1, j, 2, 0);
                if (j < n)
                    addedge(i, j, 1, i, j + 1, 2, 0);
                if (i > 1)
                    addedge(i, j, 1, i - 1, j, 2, b);
                if (j > 1)
                    addedge(i, j, 1, i, j - 1, 2, b);
            } else {
                for (int l = 2; l <= k + 1; l++) {
                    addedge(i, j, l, i, j, 1, a + c);
                }
                for (int l = 1; l <= k; l++) {
                    if (i < n)
                        addedge(i, j, l, i + 1, j, l + 1, 0);
                    if (j < n)
                        addedge(i, j, l, i, j + 1, l + 1, 0);
                    if (i > 1)
                        addedge(i, j, l, i - 1, j, l + 1, b);
                    if (j > 1)
                        addedge(i, j, l, i, j - 1, l + 1, b);
                }
            }
        }
    }
    return;
}  //分层图最短路的存图都好恶心啊。

void dij() {
    memset(dis, 0x3f, sizeof(dis));
    memset(use, false, sizeof(use));
    dis[1] = 0;
    q.push(make_pair(0, 1));
    while (!q.empty()) {
        int now = q.top().second;
        q.pop();
        if (use[now])
            continue;
        use[now] = true;
        for (int i = head[now]; i; i = edge[i].next) {
            int y = edge[i].to;
            int z = edge[i].val;
            if (dis[y] > dis[now] + z) {
                dis[y] = dis[now] + z;
                q.push(make_pair(-dis[y], y));
            }
        }
    }
    return;
}

int main() {
    build();
    dij();
    int ans = maxd;
    for (int i = 1; i <= k + 1; i++) {
        ans = min(ans, dis[n * n * i]);
    }
    printf("%d\n", ans);
    // system("pause");
    return 0;
}

Question 10

Simplify the question:

Given a graph, some edges are undirected edges, some are directed edges, directed edges will not appear in the ring, and may have negative weights.

Now given the starting point s, find the shortest length from s to all other points.

Any graph with negative weight edges cannot use dijksral and the data goes through a special construction card spfa.

Therefore, it is necessary to elicit a fairy method, using the undirected edge of this problem without negative weights, and the unidirectional edge will not appear in the ring, and then seek dij in each connected block.

Then use topological sorting to deal with the distance between the connected blocks. First add all undirected edges to the graph.

Then find all the connected blocks, shrink the connected blocks, and then add the directed edges, according to the topological sorting.

Algorithm flow:

  1. Add bidirectional edges to the graph, determine all the connected blocks, and color them.
  2. Add one-way edges to the graph to determine the in-degree and out-degree of all the connected blocks. Only when the in-degree of the connected block where \ (S \) is located is \ (0 \) , there is a solution.
  3. Starting topological sorting, there is only \ (c [S] \) communication block in the initial queue \ (q \) , and the \ (dist \) array is created at the same time , \ (dist [s] = 0 \) .
  4. Continuously take out the head of the Unicom block, and perform heap optimization in the Unicom block (dij \) .
    1. Add all the nodes in the Unicom block to the heap.
    2. Remove the smallest node of \ (d [x] \) from the heap . If \ (x \) is already in the shortest set, continue.
    3. Traverse all the edges of x \ ((x, y, z) \) and relax. If \ (y \) is inside the connected block and \ (y \) is updated, then insert \ (y \) into the heap .
    4. If \ (y \) is from another Unicom block, then \ (in [c [y]]-\) , if it is reduced to \ (0 \) , add \ (c [y] \) to the end of the team.

You may have doubts because the negative weight side of connecting two connected blocks is not dealt with here.

In fact, we have already dealt with it (a face of dumbfounding.jpg) , see the notes specifically.

Then you can digress.

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cmath>
#include <queue>
#define INF 0x3f3f3f3f
#define N 25010
#define M 150010
using namespace std;

int n, m1, m2, s;
int ru[N], dis[N], col[N], head[N], cnt = 0, totc = 0;
bool vis[N];
struct Edge {
    int nxt, to, val;
} ed[M];
priority_queue<pair<int, int> > q;
queue<int> qq;

int read() {
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
    while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    return x * f;
}

void addedge(int x, int y, int z) {
    cnt++;
    ed[cnt].nxt = head[x];
    ed[cnt].to = y;
    ed[cnt].val = z;
    head[x] = cnt;
    return;
}

void dfs(int x) {
    for (int i = head[x]; i; i = ed[i].nxt) {
        int y = ed[i].to;
        if (!col[y]) {
            col[y] = totc;
            dfs(y);
        }
    }
    return;
}

void dij() {
    while (!q.empty()) {
        int now = q.top().second;
        q.pop();
        if (vis[now])
            continue;
        vis[now] = true;
        for (int i = head[now]; i; i = ed[i].nxt) {
            int y = ed[i].to, z = ed[i].val;
            if (dis[y] > dis[now] + z) {
                dis[y] = dis[now] + z;
                //这里是先松弛,在判断是否在一个连通块。
                //也就是说,当遇到连接两个连通块的负权边时,会同样进行松弛但不会入队。
                //虽然 Dijkscal 不能处理负权边,但是如果不进队仅仅是松弛操作,它还是可以胜任的。
                //这里就巧妙的讲负权边也包括在内了。
                if (col[now] == col[y])
                    q.push(make_pair(-dis[y], y));
            }
            if (col[now] != col[y] && !--ru[col[y]])
                qq.push(col[y]);
        }
    }
    return;
}

int main() {
    n = read();
    m1 = read();
    m2 = read();
    s = read();
    int x, y, z;
    for (int i = 1; i <= m1; i++) {
        x = read();
        y = read();
        z = read();
        addedge(x, y, z);
        addedge(y, x, z);
    }
    for (int i = 1; i <= n; i++) {
        if (!col[i]) {
            col[i] = ++totc;
            dfs(i);
        }
    }
    for (int i = 1; i <= m2; i++) {
        x = read();
        y = read();
        z = read();
        addedge(x, y, z);
        ++ru[col[y]];
    }
    qq.push(col[s]);
    for (int i = 1; i <= totc; i++)
        if (!ru[i])
            qq.push(i);
    memset(dis, 127, sizeof(dis));
    memset(vis, false, sizeof(vis));
    dis[s] = 0;
    while (!qq.empty()) {
        int i = qq.front();
        qq.pop();
        for (int j = 1; j <= n; j++)
            if (col[j] == i)
                q.push(make_pair(-dis[j], j));
        dij();
    }
    for (int i = 1; i <= n; i++) {
        if (dis[i] > INF)
            printf("NO PATH\n");
        else
            printf("%d\n", dis[i]);
    }
    return 0;
}

Guess you like

Origin www.cnblogs.com/lpf-666/p/12683161.html