1. 程式人生 > >poj 3635 BFS 最短路變形

poj 3635 BFS 最短路變形

【解題報告】
題意比較明確。給定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; }