UVALive 4080 Warfare And Logistics(最短路树)

本文探讨了一个涉及图论的复杂问题,通过Floyd算法和Dijkstra算法求解最短路径,并在此基础上提出一种高效算法来优化边的选择,以最大化特定计算结果。文章详细介绍了算法的实现步骤,包括预处理阶段的Dijkstra应用,以及关键的边选择逻辑,特别关注了如何在考虑最短路径树的基础上进行边的删除操作。同时,文章还指出了在处理有重边的情况下需要进行的额外处理,以确保算法的正确性和效率。
摘要由CSDN通过智能技术生成

题目:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2081

题目大意:给你n个点m条双向边的图。d(i,j)表示以 i 为源点时,j 到i的最短路。让你算 c = d(1,1)+d(1,2)+..+.d(1,n)+d(2,1)+d(2,2)+...+d(2,n)+......+d(n,n),如果d(i,j)不连通, + 的时候就用输入的 l 代替,。然后你可以删掉一条边,使得上面那个 c 最大,并求出这个最大值。

解题思路:第一个问题很好解决,floyd 和 n 次 dij 或者 spfa 都可以。关键是删掉一条边那里。如果枚举删掉的边,那复杂度就是 m*n^3 ,换 dij 和 spfa 应该也差不多,都过不了。此时就要想到,不是无论删除哪条边,所有东西都要重新算的,只有那些在最短路树上的边被删除时,才需要重新算。所以就是先 dij 处理出 n 个最短路树和每个为源点时的最短路和。然后就是上面的步骤了,先判断要不要重新算就好了。这里有个陷阱的,就是有重边。如果你要删除了两个点之间的一条边,那么肯定是删的最短的那一条,因为要使 c 最大么,删掉后不是就没有边了,应该是第二短的边顶上,所以这里还要稍微处理一下。

其实我不会算上面这个算法的复杂度,求哪位好心的大神指点~~~

代码如下:

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

typedef long long lld;

const int INF = 0x0fffffff;

const int MAXN = 111;

vector<int> G[MAXN][MAXN];

bool flag[MAXN][MAXN][2];

void init(int n)
{
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)
            G[i][j].clear();
}

void add_edge(int s,int t,int val)
{
    G[s][t].push_back(val);
    if(G[s][t].size() <= 2) flag[s][t][G[s][t].size()-1] = 1;
}

struct  Node
{
    int id;
    int val;
    Node(int iid,int vval)
    {
        id = iid;
        val = vval;
    }
    bool operator < (const Node& tmp) const
    {
        return val > tmp.val;
    }
};

priority_queue<Node> q;

bool done[MAXN];

int d[MAXN];
int p[MAXN][MAXN];

int first;

void dij(int s,int n)
{
    for(int i = 1;i <= n;i++)
        d[i] = INF;
    d[s] = 0;
    p[s][s] = -1;
    q.push(Node(s,0));
    memset(done,0,sizeof(done));
    while(!q.empty())
    {
        Node cur = q.top();
        q.pop();
        if(done[cur.id]) continue;
        done[cur.id] = 1;
        for(int i = 1;i <= n;i++)
            for(int j = 0;j < G[cur.id][i].size();j++)
            {
                if(flag[cur.id][i][j])
                {
                    int tmp = cur.val+G[cur.id][i][j];
                    if(tmp < d[i])
                    {
                        d[i] = tmp;
                        if(first) p[s][i] = cur.id;
                        q.push(Node(i,tmp));
                    }
                    break;
                }
            }
    }
}

lld cc[MAXN];

int main()
{
    int n,m,l;
    while(~scanf("%d%d%d",&n,&m,&l))
    {
        init(n);
        while(m--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add_edge(a,b,c);
            add_edge(b,a,c);
        }
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= n;j++)
                sort(G[i][j].begin(),G[i][j].end());
        lld c = 0;
        first = 1;
        for(int i = 1;i <= n;i++)
        {
            dij(i,n);
            cc[i] = 0;
            for(int j = 1;j <= n;j++)
            {
                if(d[j] >= INF) cc[i] += l;
                else cc[i] += d[j];
            }
            c += cc[i];
        }
        first = 0;
        printf("%lld ",c);
        lld ans = 0;
        for(int i = 1;i <= n;i++)
        {
            for(int j = i+1;j <= n;j++)
                if(G[i][j].size())
                {
                    flag[i][j][0] = flag[j][i][0] = 0;
                    lld tmp = 0;
                    for(int s = 1;s <= n;s++)
                        if(p[s][i] == j || p[s][j] == i)
                        {
                            dij(s,n);
                            for(int k = 1;k <= n;k++)
                                if(d[k] >= INF) tmp += l;
                                else tmp += d[k];
                        }
                        else tmp += cc[s];
                    flag[i][j][0] = flag[j][i][0] = 1;
                    ans = max(ans,tmp);
                }
        }
        printf("%lld\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值