13年浙江省赛 Highway Project 【最短路】

该博客讨论了在确保从0号点出发的单源最短路径条件下,如何以最小成本选择边的问题。作者最初误以为可以应用最短路上的最小生成树策略,但后来意识到由于边是单向的,导致问题不能简单解决。正确的解决方案是在Dijkstra算法中调整判断条件,或者遍历最短路径上的边,记录每个点的最小入边成本并求和,以确保在单起点有向图中可达所有点。

·题目链接:https://vjudge.net/problem/ZOJ-3946

 

题目大意:有N个点,编号0到N-1,有M条可选的双向边,边有长度和花费。在保证以0为起点的单源最短路的前提下,使选择的边的花费最小。

 

一眼过去想到的是 对最短路上的边求最小生成树。

感觉挺对的,写了很长时间wa了两发后队友指出了发现了错误。

首先,最短路选边之后是一个单起点DAG图(把原图中的双向边拆成2条单向边),最小生成树是按双向边考虑的,但实际上边是单向的,所以并不能保证0号点到任意点可达。

 

正确做法:

一:直接dij里修改判断条件。

二:遍历最短路上的边,记录某个点的入边的最小花费,然后求和。因为这是一个单起点DAG,所以这样做最后一定可达。

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a);i <= (b); i++)
#define mp make_pair
#define pb push_back
#define ll long long
#define pli pair<long long,int>
#define all(x) (x).begin(),(x).end()
using namespace std;
const int N = 1e5+10000;
struct node{
    int u, v, nxt;
    ll len, c;
    node(){}
    node(int a,int b,ll c1,ll d,int k) {
        u = a; v = b; len = c1; c = d; nxt = k;
    }
};
int tot=0;
ll dis[N];
int head[3*N];
ll f[N];
node edge[3*N];
vector<node> M_edge;
void add_edge(int u, int v, ll len, ll c) {
    tot++;
    edge[tot] = node(u,v,len,c,head[u]);
    head[u] = tot;
}
void dij(int u) {
    dis[u] = 0;
    priority_queue<pli> q;
    q.push(mp(-dis[u],u));
    while(!q.empty()) {
        pli x = q.top();
        q.pop();
        int u = x.second;
        for(int i = head[u]; i != -1; i = edge[i].nxt) {
            int v = edge[i].v;
            ll w = edge[i].len;
            if(dis[v]>dis[u]+w) {
                dis[v] = dis[u]+w;
                q.push(mp(-dis[v],v));
            }
        }
    }
}
int main() {
   // freopen("a.txt","r",stdin);
    ios::sync_with_stdio(0);
    int T;
    cin>>T;
    while(T--) {
        int n, m;
        cin>>n>>m;
        tot = 0;
        memset(head,-1,sizeof(head));
        rep(i, 1, m) {
            int u, v;
            ll len, c;
            cin>>u>>v>>len>>c;
            add_edge(u,v,len,c);
            add_edge(v,u,len,c);
        }
        rep(i, 0, n-1) dis[i] = 1e18;

        dij(0);
        ll sum=0;
        rep(i, 0, n-1) sum += dis[i];

        M_edge.clear();
        rep(i, 1, tot) {
            int u = edge[i].u;
            int v = edge[i].v;
            if(dis[v]-dis[u] ==edge[i].len) M_edge.pb(edge[i]);
        }

        ll cost=0;
        rep(i, 0, n-1) f[i] = 1e9;
        f[0] = 0;
        for(auto x:M_edge) {
            int v = x.v;
            f[v] = min(f[v],x.c);
        }
        rep(i, 0, n-1) cost += f[i];
        cout<<sum<<' '<<cost<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值