AOJ 2541 Magical Bridges

21 篇文章 0 订阅
该博客讨论了AOJ 2541问题,涉及n个岛屿间的m条桥,其中k条是魔法桥。目标是找到在使用魔法调整魔法桥长度后,从S1和S2到T的最短路径最小绝对差。通过将最短路径转化为ax+b的形式,并考虑所有直线的最低点,博主提出了使用Dijkstra算法预处理并暴力求解线段交点的方法,其时间复杂度为O(k^3)。
摘要由CSDN通过智能技术生成


Aizu 2541

题意:n个岛屿,由m条桥连接,其中有k条是魔法桥,你可以用魔法把他们变成相同长度。求在执行魔法后,两个起点S1和S2到终点T的最短路的最小绝对差。

(1<=n<=1000,1<=m<=2000,1<=k<=100)


S1和S2到T的最短路将会是如 ax+b 的形式。x为相同长度,a为该最短路上魔法桥的个数。

画出所有的直线,现在等价于求多条射线的最低点。

利用线段交暴力即可。

用dij进行预处理,每个点可以得到最多k条直线,暴力求交点的复杂度O(k^2)。

最后要判断最低点,除去dij处理外,复杂度为O(k^3)。


#include <bits/stdc++.h>
using namespace std;
const long long INF=1LL<<60;
const int MAXN=1005;
int n, m, S1, S2, T;
char s[100];
struct edge{
    int to;
    long long w;
    bool type;
    edge(int to=0, long long w=0, bool type=false):to(to),w(w),type(type) {}
};
vector<edge> G[MAXN];
long long dist[MAXN][MAXN];
priority_queue< pair<long long, pair<int, int> > > q;
pair<long long, pair<int, int> > now;
vector<long long> ans;
long long cross(long long a1, long long b1, long long a2, long long b2){
    if(a1==INF||a2==INF) return 0;
    return floor((a1-a2+1.0)/(b2-b1));
}
long long calc(long long dp[MAXN], int k, long long x){
    long long ret=INF;
    for(int i=0; i<=k; i++){
        ret=min(ret,dp[i]+x*i);
    }
    return ret;
}
int main(){
    while(~scanf("%d%d%d%d%d",&n,&m,&S1,&S2,&T)&&n){
        for(int i=1; i<=n; i++){
            G[i].clear();
        }
        int k=0;
        for(int i=0, x, y, z; i<m; i++){
            scanf("%d%d%s",&x,&y,s);
            if(s[0]=='x'){
                G[x].push_back(edge(y,0,1));
                G[y].push_back(edge(x,0,1));
                k++;
            }
            else{
                sscanf(s,"%d",&z);
                G[x].push_back(edge(y,z,0));
                G[y].push_back(edge(x,z,0));
            }
        }
        for(int i=1; i<=n; i++){
            for(int j=0; j<=k; j++){
                dist[i][j]=INF;
            }
        }
        dist[T][0]=0;
        while(!q.empty()) q.pop();
        q.push(make_pair(0LL,make_pair(T,0)));
        while(!q.empty()){
            now=q.top(); q.pop();
            int u=now.second.first, cnt=now.second.second;
            long long d=dist[u][cnt];
            if(-now.first>d) continue;
            for(auto p: G[u]){
                int ncnt=cnt+p.type, nidx=p.to;
                long long ndist=d+p.w;
                if(ncnt<=k&&ndist<dist[nidx][ncnt]){
                    dist[nidx][ncnt]=ndist;
                    q.push(make_pair(-ndist,make_pair(nidx,ncnt)));
                }
            }
        }
        ans.clear();
        ans.push_back(0LL);
        for(int i=0; i<=k; i++){
            for(int j=0; j<=k; j++) if(i!=j){
                long long x1, x2, x3;
                x1=cross(dist[S1][i],i,dist[S2][j],j);
                x2=cross(dist[S1][i],i,dist[S1][j],j);
                x3=cross(dist[S2][i],i,dist[S2][j],j);
                for(int d=-1; d<=1; d++){
                    ans.push_back(x1+d);
                    ans.push_back(x2+d);
                    ans.push_back(x3+d);
                }
            }
        }
        sort(ans.begin(),ans.end());
        ans.erase(unique(ans.begin(),ans.end()),ans.end());
        long long ret=INF;
        for(auto p: ans){
            if(p>=0){
                ret=min(ret,abs(calc(dist[S1],k,p)-calc(dist[S2],k,p)));
            }
        }
        printf("%lld\n",ret);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值