標籤:最短路
Toll! Revisited
大致題意:有兩種節點,一種是大寫字母,一種是小寫字母。首先輸入m條邊,當經過小寫字母時需要付一單位的過路費,當經過大寫字母時,要付當前財務的1/20做過路費。問在起點最少需要帶多少物品使到達終點時還有k個物品。當有多條合格路徑時輸出字典序最小的一個。
思路:已知終點的權值,那麼可以從終點向前推。求終點到起點的最短路徑,然後按字典序列印路徑。
比較難處理的是:向前推時前驅節點的權值計算。列個方程算算就可以了,主要時不能整除的情況。
計算前驅結點dis值的時候,同時記錄(i,j)的邊權值,這是列印路徑的依據。
#include <stdio.h>#include <algorithm>#include <set>#include <map>#include <vector>#include <math.h>#include <string.h>#include <stack>#include <queue>#define LL long long#define _LL __int64using namespace std;const int INF = 0x3f3f3f3f;const int maxm = 1010;const int maxn = 60;struct node{ int v,w; int next;}edge[maxm];int m;int pre[maxn],cnt;int start,end;LL dis[maxn],p;int vis[maxn];vector <int> ans;void init(){ cnt = 0; memset(pre,-1,sizeof(pre)); for(int i = 0; i < maxm; i++) edge[i].w = 0;}void add(int u, int v){ edge[cnt].v = v; edge[cnt].next = pre[u]; pre[u] = cnt++;}void dijstra(){ priority_queue <pair<LL,int>, vector<pair<LL,int> >, greater<pair<LL,int> > > que; while(!que.empty()) que.pop(); memset(dis,INF,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[end] = p; que.push(make_pair(dis[end],end)); while(!que.empty()) { int u = que.top().second; que.pop(); if(vis[u]) continue; vis[u] = 1; for(int i = pre[u]; i != -1; i = edge[i].next) //鬆弛相鄰節點 { if(vis[edge[i].v]) continue; int v = edge[i].v; if(u < 26) { //計算前驅結點的權值,判斷是否整除,若不整除,嘗試加1繼續判斷 if(dis[u]%19 == 0) { if(dis[v] > dis[u]/19*20) { dis[v] = dis[u]/19*20; edge[i].w = edge[i^1].w = dis[v]-dis[u]; que.push(make_pair(dis[v],v)); } } else if( (dis[u]+1)%19 ) { if(dis[v] > (dis[u]+1)*20/19) { dis[v] = (dis[u]+1)*20/19; edge[i].w = edge[i^1].w = dis[v]-dis[u]; que.push(make_pair(dis[v],v)); } } else { if(dis[v] > (dis[u]+1)*20/19-1 ) { dis[v] = (dis[u]+1)*20/19-1; edge[i].w = edge[i^1].w = dis[v]-dis[u]; que.push(make_pair(dis[v],v)); } } } else { if(dis[v] > dis[u]+1) { dis[v] = dis[u]+1; edge[i].w = edge[i^1].w = 1; que.push(make_pair(dis[v],v)); } } } }}void solve(){ ans.clear(); int now; now = start; ans.push_back(now); while(now != end) { int tmp = 1<<6; for(int i = pre[now]; i != -1; i = edge[i].next) { int v = edge[i].v; //因為輸出字典序最小的,所以求出滿足dis[now] - dis[v] == edge[i].w中最小的v if(dis[now] - dis[v] == edge[i].w && v < tmp) { tmp = v; } } ans.push_back(tmp); now = tmp; } printf("%c",ans[0]+‘A‘); for(int i = 1; i < (int)ans.size(); i++) printf("-%c",ans[i]+‘A‘); printf("\n");}int main(){ int item = 1; char t1,t2; while(~scanf("%d",&m)) { if(m == -1) break; init(); getchar(); for(int i = 0; i < m; i++) { scanf("%c %c",&t1,&t2); getchar(); add(t1-‘A‘,t2-‘A‘); add(t2-‘A‘,t1-‘A‘); } scanf("%lld %c %c",&p,&t1,&t2); start = t1-‘A‘; end = t2-‘A‘; dijstra(); printf("Case %d:\n",item++); printf("%lld\n",dis[start]); solve(); } return 0;}