Codeforces667D(spfa+dp)

题意:
给定一个带权有向图,若P(A,B)表示节点A到B的最短路长度,选择四个节点ABCD,使得P(A,B)+P(B,C)+P(C,D)最大。
节点数n在1,000以内,边数m在2,000以内。
思路:
首先先将两两点之间的最短路都算出来。
之后建立pre和next两个数组,但在这里我用结构体保存其权值和编号一直算不出正确结果,退而求其次,只保存最长路径排名前三的结点,每次遇到一个更长的边,就将另外两条边往后推一个位置,给新的最长边留出位置。
在枚举的时候,当最长边不存在的时候、最长边对应的点与b、c点冲突的时候,两个最长边对应的点彼此冲突的时候,跳过此次枚举。
最后直接输出对应的结果编号即可。

#include<cstdio>
#include<vector>
#include<queue>
using namespace std;

const int inf = 99999999;
const int maxn = 10000 + 5;

struct edge { 
    int v, w; 
};
int nex[maxn][3], pre[maxn][3];
vector<vector<edge>>a;
int n, m, dis[maxn][maxn];
bool flag[maxn][maxn];

void add_edge(int u, int v, int w) {
    edge tmp;
    tmp.v = v;
    tmp.w = w;
    a[u].push_back(tmp);
}

void spfa(int s) {
    queue<int>q;
    q.push(s);
    dis[s][s] = 0; flag[s][s] = true;
    while (!q.empty()) {
        int u = q.front(); q.pop(); flag[s][u] = false;
        for (int i = 0; i < a[u].size(); i++) {//扫描所有邻接点
            if (dis[s][a[u][i].v] > dis[s][u] + a[u][i].w) {
                dis[s][a[u][i].v] = dis[s][u] + a[u][i].w;
                if (!flag[s][a[u][i].v]) {
                    q.push(a[u][i].v);
                    flag[s][a[u][i].v] = true;
                }
            }
        }
    }
}

void upd() {
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < 3; j++) {
            nex[i][j] = -1; 
            pre[i][j] = -1;
        }
    }
    int i, j;
    for (i = 1; i <= n; i++){
        for (j = 1; j <= n; j++){
            if (dis[i][j] == inf) continue;
            if (nex[i][0] == -1 || dis[i][nex[i][0]]<dis[i][j]){
                nex[i][2] = nex[i][1];
                nex[i][1] = nex[i][0];
                nex[i][0] = j;
            }
            else if (nex[i][1] == -1 || dis[i][nex[i][1]]<dis[i][j]){
                nex[i][2] = nex[i][1];
                nex[i][1] = j;
            }
            else if (nex[i][2] == -1 || dis[i][nex[i][2]]<dis[i][j]){
                nex[i][2] = j;
            }
        }
    }
    for (i = 1; i <= n; i++){
        for (j = 1; j <= n; j++){
            if (dis[j][i] == inf) continue;
            if (pre[i][0] == -1 || dis[pre[i][0]][i]<dis[j][i]){
                pre[i][2] = pre[i][1];
                pre[i][1] = pre[i][0];
                pre[i][0] = j;
            }
            else if (pre[i][1] == -1 || dis[pre[i][1]][i]<dis[j][i]){
                pre[i][2] = pre[i][1];
                pre[i][1] = j;
            }
            else if (pre[i][2] == -1 || dis[pre[i][2]][i]<dis[j][i]){
                pre[i][2] = j;
            }
        }
    }
}

int main() {
    int u, v, w;
    scanf("%d%d", &n, &m);
    a.resize(maxn);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            dis[i][j] = inf;
            flag[i][j] = false;
        }
    }

    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d", &u, &v, &w);
        add_edge(u, v, w);
    }
    for (int i = 1; i <= n; i++) {
        spfa(i);
    }
    upd();
    int ans = 0, str[4];
    for (int b = 1; b <= n; b++) {
        for (int c = 1; c <= n; c++) {
            if (b == c || dis[b][c] == inf) continue;
            for (int k = 0; k < 3; k++) {
                for (int l = 0; l < 3; l++) {
                    if (pre[b][k] == -1 || nex[c][l] == -1)continue;
                    if (pre[b][k] == b || pre[b][k] == c)continue;
                    if (nex[c][l] == b || nex[c][l] == c)continue;
                    if (pre[b][k] == nex[c][l])continue;
                    int tem = dis[pre[b][k]][b] + dis[b][c] + dis[c][nex[c][l]];
                    if (tem > ans)
                    {
                        ans = tem;
                        str[0] = pre[b][k]; str[3] = nex[c][l];
                        str[1] = b; str[2] = c;
                    }
                }
            }
        }
    }
    printf("%d %d %d %d\n", str[0], str[1], str[2], str[3]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38995588/article/details/80621429