UVa11367.Full Tank?(Dijkstra+DP)

题目链接:https://vjudge.net/problem/UVA-11367

题目大意:

给出n个地点 和 每个地点的油价 ,有 m 条边 , 并给出每条边长度 。1单位汽油可以走1千米  , 油箱的容量为 c , 在初始点 s 时 , 油箱中的油为 0 , 求s 到 t 的最小花费 。

思路:

因为并不能确定这个点是否需要加油,这不是一个单调性的问题,所以需要dp一下,用分层图的思想,dis[i][j]表示到达i这个点邮

箱内还有j升油的最小花费,那么就是一个很裸的分层图了,对于每个点,遍历每条边时可以加多少油都是一种状态。

但是!如果你暴力的加所有的状态的话,毫无疑问会超时,因为Heap中的节点实在是太多了,而有用的节点没有多少个。

所以,就像之前任意起点终点k短路那样,我们并不需要加所有的状态,而是像分层图那样,在当前节点u一升一升地加油,如果可

以走哪条边的就转移过去。这样可以避免很多没有用的节点,也就不会超市了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int maxn=1000+10;
struct Edge{
    int from,to,cost;
    Edge(int u,int v,int cost):from(u),to(v),cost(cost){}
};
vector<int>G[maxn];
vector<Edge>edges;
int p[maxn];//油价
ll dis[maxn][110];//表示到达i点还剩j升油的最小花费
int vis[maxn][110];
int n,m;
const ll INF=0x3f3f3f3f3f3f;
struct HeapNode{
    int u;
    ll d;
    int c;
    HeapNode(int u,ll d,int c):u(u),d(d),c(c){}
    bool operator < (const HeapNode &rhs)const{
        return d>rhs.d;
    }
};
void add_edges(int u,int v,int cost){
    edges.push_back(Edge(u,v,cost));
    edges.push_back(Edge(v,u,cost));
    int sz=edges.size();
    G[u].push_back(sz-2);
    G[v].push_back(sz-1);
}
ll Dijkstra(int s,int t,int c){
    memset(vis,0,sizeof(vis));
    for(int i=0;i<=n;i++){
        for(int j=0;j<=c;j++){
            dis[i][j]=INF;
        }
    }
    priority_queue<HeapNode>Q;
    Q.push(HeapNode(s,0,0));
    vis[s][0]=1;
    dis[s][0]=0;
    while(Q.size()){
        HeapNode x=Q.top();Q.pop();
        int u=x.u;
        for(int i=0;i<G[u].size();i++){
            Edge &e=edges[G[u][i]];
            if(x.c>=e.cost){//可以转移
                if(dis[e.to][x.c-e.cost]>dis[u][x.c]){
                    dis[e.to][x.c-e.cost]=dis[u][x.c];
                    if(!vis[e.to][x.c-e.cost]){
                        Q.push(HeapNode(e.to,dis[e.to][x.c-e.cost],x.c-e.cost));
                        vis[e.to][x.c-e.cost]=1;
                    }
                }
            }
        }
        if(x.c<c){
            if(dis[u][x.c+1]>dis[u][x.c]+p[u]){
                dis[u][x.c+1]=dis[u][x.c]+p[u];
                if(!vis[u][x.c+1]){
                    Q.push(HeapNode(u,dis[u][x.c+1],x.c+1));
                }
            }
        }
    }
    ll ans=INF;
    for(int i=0;i<=c;i++){
        ans=min(ans,dis[t][i]);
    }
    return ans;
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%lld",&p[i]);
    }
    for(int i=1;i<=m;i++){
        int u,v,cost;
        scanf("%lld%lld%lld",&u,&v,&cost);
        add_edges(u,v,cost);
    }
    int q;
    for(scanf("%lld",&q);q--;){
        int s,t,c;
        scanf("%lld%lld%lld",&c,&s,&t);
        ll ans=Dijkstra(s,t,c);
        if(ans==INF){
            puts("impossible");
        }
        else{
            printf("%lld\n",ans);
        }
    }
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值