LA 4080 Warfare And Logistics

本文探讨了一种通过删除特定边来最大化最短路径树权重的问题。利用Dijkstra算法改进复杂度,介绍了如何标记最短路径树中的边,并计算删除不同边后的路径权重,最终找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要删除一条边之后使得c最大。

首先我们要知道,当求出最短路树后,删除不在树上的边最短路是不会变化的。因为n个点的最短路树有n - 1条边,所以对每一个节点枚举要删除的边的复杂度可以由O(m)降为O(n)。

如果用floyd,求得每对顶点间的最短路复杂度是O(n^3),然后对每一个点删边,总复杂度是O(n^5)。这样果断是不行的,然后就想到dijkstra。

用dijkstra求单源最短路的复杂度是O(mlogn),所以总的复杂度是O(n^2*m*logn),比floyd要好。

注意重边的处理,最好用邻接表存。

#include <iostream>
#include<vector>
#include<stdio.h>
#include<cstring>
#include<queue>
#define maxn 110
using namespace std;
int n,m,l;
struct Edge
{
    int from,to,dist,v,exi,cost;
    //v:是否是最短路树边
    //exi:当前是否删除这条边
    //cost:删除这条边的代价
};
struct HeapNode
{
    int d,u;
    bool operator < (const HeapNode& rhs) const
    {
        return d > rhs.d;
    }
};
vector<Edge>edges;
vector<int>G[maxn];
int vis[maxn];
int d[maxn];
int p[maxn];
int ans1,ans2,c1,c2;

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

void add_edge(int from,int to,int dist)
{
    edges.push_back((Edge)
    {
        from,to,dist
    });
    int mm = edges.size();
    G[from].push_back(mm - 1);
}

void dijkstra(int s)
{
    priority_queue<HeapNode>Q;
    for(int i = 1; i <= n; i++) d[i] = l;
    d[s] = 0;
    memset(vis,0,sizeof vis);
    memset(p,-1,sizeof p);
    Q.push((HeapNode)
    {
        0,s
    });
    while(!Q.empty())
    {

        HeapNode x = Q.top();
        Q.pop();
        int u = x.u;
        if(vis[u]) continue;
        vis[u] = 1;
        for(int i = 0; i < G[u].size(); i++)
        {

            Edge& e = edges[G[u][i]];
            if(e.exi == 0) continue;

            if(d[e.to] > d[e.from] + e.dist)
            {
                d[e.to] =  d[e.from] + e.dist;
                p[e.to] = G[u][i];
                Q.push((HeapNode)
                {
                    d[e.to],e.to
                });
            }
        }
    }
}


void mark(int s) //标记最短路树边
{
    if(p[s] == -1) return;
    Edge& e = edges[p[s]];
    if(e.v == 0)
    {
        e.v = 1;
        int tmp = p[s] - 1; //标记它的反响边
        if(tmp >= 0)
        {
            Edge& e1 = edges[tmp];
            if(e.from == e1.to && e.to == e1.from && e1.v == 0)
            {
                e1.v = 1;
            }
        }
        tmp = p[s] + 1;

        if(tmp < 2 * m)
        {
            Edge& e2 = edges[tmp];
            if(e.from == e2.to && e.to == e2.from && e2.v == 0)
            {
                e2.v = 1;
            }
        }
    }
    s = e.from;
}

int calc() //计算最短路之和
{
    int tmp = 0;
    for(int i = 1; i <= n; i++)
    {
        tmp+=d[i];
    }
    return tmp;
}

void work(int s)
{
    for(int i = 0; i < edges.size(); i++)
    {
        edges[i].v = 0;
        edges[i].exi = 1;
    }
    dijkstra(s);
    c1 = calc();
    ans1+=c1;
    for(int i = 1; i <= n; i++)
        mark(i);

    for(int i = 0; i < edges.size(); i+=2) //因为每条边存了两遍(正反向),所以+2
    {
        Edge& e = edges[i];
        Edge& e1 = edges[i + 1];
        if(e.v)//如果是最短路树边
        {
            e.exi = 0;
            e1.exi = 0;
            dijkstra(s);
            e.exi = 1; //记得一定要还原!!
            e1.exi = 1;
            int tmp = calc();
            e.cost+=tmp;
            e1.cost+=tmp;
        }
        else //不然最短路没有变化
        {
            e.cost+=c1;
            e1.cost+=c1;
        }

    }
}

int main()
{
    while(scanf("%d %d %d",&n,&m,&l)!=EOF)
    {
        init();
        for(int i = 1; i <= m; i++)
        {
            int from,to,dist;
            scanf("%d %d %d",&from,&to,&dist);
            add_edge(from,to,dist);
            add_edge(to,from,dist);
        }
        for(int i = 1; i <= n; i++)
        {
            work(i);
        }
        for(int i = 0; i < edges.size(); i++)
        {
            ans2 = max(ans2,edges[i].cost);
        }
        printf("%d %d\n",ans1,ans2);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值