BAPC 2014(Highway Hassle-加油站问题)

已知一个连通无向图,其中一些点有加油站(每个加油站油价不同),现在S点有一个油箱大小为t前往T的车。求最小油费花销。

经典题.考虑从A加油站前往B加油站,要么在A加满,要么在B恰好用完。因此可以拆点最短路。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define IN freopen("in.txt", "r", stdin)
#define OUT freopen("out.txt", "w", stdout)
#define scan(x) scanf("%d", &x)
#define mp make_pair
#define pb push_back
#define sqr(x) (x) * (x)
#define pr(x) printf("Case %d: ",x)
#define prn(x) printf("Case %d:\n",x)
#define prr(x) printf("Case #%d: ",x)
#define prrn(x) printf("Case #%d:\n",x)
#define lowbit(x) (x&(-x))
#define fi first
#define se second
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;

const int maxn=1005;
int dis[maxn][maxn];
struct edge
{
    int to,cost;
    bool operator < (const struct edge &p)const
    {return cost>p.cost;}
};
vector<edge> g[maxn];
priority_queue<edge> q;
bitset<maxn> vis;
int n,m,k,T,lim;
pii sta[125];

void Dijkstra(int st)
{
    vis.reset();
    for(int i=1;i<=n;i++)dis[st][i]=1e9;
    dis[st][st]=0;
    while(!q.empty())q.pop();
    q.push({st,0});
    while(!q.empty())
    {
        edge t=q.top();q.pop();
        int id=t.to;
        vis.set(t.to);
        for(edge p : g[id])
            if(dis[st][p.to]>dis[st][id]+p.cost)
            {
                dis[st][p.to]=dis[st][id]+p.cost;
                q.push({p.to,dis[st][p.to]});
            }
        while(!q.empty() && vis.test(q.top().to))q.pop();
    }
}

typedef long long ll;
typedef pair<ll,pii > plii;

#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=Pre[x];p;p=Next[p])
#define INF (1000000000000000LL)
#define MEM(a) memset(a,0,sizeof(a));
int cnt;
pii p[300*122*2];
int find(int x,int y) {
    return lower_bound(p+1,p+1+cnt,mp(x,y))-p-1;
}
struct Edge{
    int from,to;
    ll dist;
};
struct HeapNode {
    ll d;
    int u;
    bool operator< (const HeapNode& rhs) const {
        return d > rhs.d;
    }
}; 

struct Dijkstr {
    int n,m;
    #define MAXN (300*122*2)
    vector<Edge> edges;
    vector<int> G[MAXN];
    bool done[MAXN];
    ll d[MAXN];
    int p[MAXN]; //最短路中上一条边
    int pnode[MAXN];
    void addedge(int u,int v,ll w){
        edges.pb((Edge){u,v,w});
        G[u].pb(m++);
    }
    void init(int _n){
        n=_n; m = 0;
        Rep(i,n) G[i].clear();
        edges.clear(); 
    } 
    void dijkstra(int s) {
        priority_queue<HeapNode> Q;
        Rep(i,n) d[i]=INF,pnode[i]=-1;
        d[s]=0;
        MEM(done)
        Q.push((HeapNode){0,s});
        while(!Q.empty()) {
            HeapNode x=Q.top(); Q.pop();
            int u=x.u;
            if (done[u]) continue;
            done[u]=1;
            int mm=G[u].size();
            Rep(i,mm) {
                Edge e = edges[G[u][i]];
                if (d[e.to]>d[u]+e.dist) {
                    d[e.to]=d[u]+e.dist;
                    p[e.to]=G[u][i];
                    pnode[e.to]=u;
                    Q.push((HeapNode){d[e.to],e.to});
                }
            }
        }
    } 
}S1;

int main()
{
//  freopen("H.data","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d",&n,&m,&k,&lim);
        for(int i=1;i<=n;i++)g[i].clear();
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            g[x].pb({y,z});g[y].pb({x,z});
        }

        for(int i=1;i<=k;i++)
        {
            scanf("%d%d",&sta[i].fi,&sta[i].se);
            Dijkstra(sta[i].fi);
        }

        int st,ed;
        scanf("%d%d",&st,&ed);
        int x=0;For(i,k) if (st==sta[i].fi) x=i;
        int y=0;For(i,k) if (st==sta[i].fi) y=i;

        ++k;
        sta[k].fi=ed,sta[k].se=0;

        Dijkstra(st);
        Dijkstra(ed);

        cnt=0;
        For(i,k) {
            p[++cnt]=mp(i,0);
            if (i==k) continue;
            p[++cnt]=mp(i,lim);
            For(j,k) {
                if (i!=j) {
                    p[++cnt]=mp(i,lim-dis[sta[j].fi][sta[i].fi]);
                    p[++cnt]=mp(i,dis[sta[j].fi][sta[i].fi]);

                }
            }
        }
        For(i,k) For(j,k) {
            if (dis[sta[j].fi][sta[i].fi]!=dis[sta[i].fi][sta[j].fi]) puts("s");
        }
        sort(p+1,p+1+cnt);
        cnt=unique(p+1,p+1+cnt)-p-1;
        S1.init(cnt);
        Fork(i,2,cnt) {
            if (p[i].fi==p[i-1].fi ) {
                S1.addedge(find(p[i-1].fi,p[i-1].se),
                find(p[i].fi,p[i].se),
                (p[i].se-p[i-1].se)*sta[p[i].fi].se );
            }
        }
        For(i,k-1) {
            For(j,k) if (i!=j){
                if (dis[sta[i].fi][sta[j].fi]<=lim) {
                    S1.addedge(find(i,dis[sta[i].fi][sta[j].fi] ) , find(j,0), 0);
                    if(j^k)
                    S1.addedge(find(i,lim),find(j,lim-dis[sta[i].fi][sta[j].fi] ),0);
                }
            }
        }
        S1.dijkstra(find(x,0));
        cout<<S1.d[find(k,0)]<<endl;

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值