poj 3635 BFS 最短路变形

本文介绍了一种利用最短路算法解决特定加油问题的方法。问题设定在一个包含N个点的图中,目标是从起点S到达终点E,同时在各点加油以确保油耗最优。通过定义状态dp[u][f]表示在节点u油量为f时的最小花费,采用松弛操作进行状态转移,最终实现从起点到终点的最低成本路径计算。

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

【题目链接】
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=16131

【解题报告】
题意比较明确。给定N(N<=1000)个点的图,要求从S到E花费最少。
其中每个点可以加油,给出每个点的油价。一个单位距离消耗一个单位油。车辆有最大储存油量。

可以把这道题目理解为是一个二维的最短路,其中这个“路”在这里并不是两点之间距离,而是两点之间花费值。
而我们在每一个点可以选择加油或者走向下一个点。
因为加油量始终为整数,所以我们可以一次只加一单位油。
设dp[u][f]表示在u节点,当前油量为f的最小花费(可以理解为从s到u的最短路)
那么之后的扩展有两个选择:
1.如果在城市u的顶点dp[u][f]加油更优(假如已经由之前某个城市更新了到这个城市的最小花费),那么我们在u加一单位油。(这是dij求最短路的松弛操作,注意求同存异)
2.可以从城市u转移到城市v。并且由u转移到v更优(同样是松弛操作)。
那么我们就更新v城市状态dp[v][f’],这里f’是u点的油量f减去u-v的路径长度。

【参考代码】

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;

const int maxn=1e3+1e2;

int mp[maxn][maxn];
vector<int>link[maxn];
int p[maxn];
int dp[maxn][110];  //dp[i][j]表示在i点,油量为j时的最小花费
int n,m;

struct Node{
      int pos,fuel,cost;
      bool operator < (  const Node& rhs )const{
            return cost>rhs.cost;
      }
};

int BFS(  int s, int e, int cap )
{
      memset(  dp, 127, sizeof dp );
      Node now; now.pos=s; now.fuel=0,now.cost=0;
      dp[now.pos][0]=0;
      priority_queue<Node>q;
      q.push( now );
      while( !q.empty() )
      {
            Node now=q.top(); q.pop();
            int u=now.pos;
            if( u==e )return now.cost;
            //可以加油并且加油更划算,加上一升油推进队列
            if(  now.fuel<cap && dp[ u ][  now.fuel+1 ]> dp[u][now.fuel]+p[u]  )
            {
                  dp[ u ][  now.fuel+1 ]=dp[u][now.fuel]+p[u];
                  Node rhs; rhs.pos=u; rhs.cost=now.cost+p[ u  ]; rhs.fuel=now.fuel+1;
                  q.push( rhs );
            }

            for( int i=0;i<link[u].size();i++ )
            {
                  int v=link[u][i];
                  int nxt=now.fuel-mp[u][v];
                  if(nxt<0)continue;
                  //可以从u到v这个城市的话,看看划算不划算
                  if(  dp[v][ nxt  ]> dp[u][ now.fuel  ]  )
                  {
                        dp[v][nxt]=dp[u][ now.fuel ];
                        Node rhs; rhs.pos=v; rhs.cost=now.cost; rhs.fuel=nxt;
                        q.push( rhs );
                  }
            }
      }
      return -1;
}

int main()
{
      while( cin>>n>>m )
      {
            memset( mp,0,sizeof mp );
            for( int i=0;i<n;i++ ) cin>>p[i];//顶点从0开始编号
            for( int i=1;i<=m;i++ )
            {
                  int u,v,d;
                  cin>>u>>v>>d;
                  mp[u][v]=mp[v][u]=d;
                  link[u].push_back(v);
                  link[v].push_back(u);
            }
            int q; cin>>q;
            for( int i=1; i<=q;i++ )
            {
                  int s,e,cap;
                  cin>>cap>>s>>e;
                  int ans=BFS(  s,e,cap );
                  if(   ans==-1  )cout<<"impossible\n";
                  else cout<<ans<<endl;
            }

      }
      return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值