Low Cost Air Travel UVALive - 3561
图论·最短路
题目大意:
有一些联票,每张票有一些站,每张票你只能从头开始乘,不能不能中途上或者下。现在有一些行程,要按顺序去一些地方。问你怎么样乘花费最小。输出最小值和路径。
题解:
由于联票的存在,不能用相邻两个城市的最短路求和了,因为几个连续的城市可能在用同一张联票。
解决方法是把当前已经走了行程单上的前pos个城市作为状态的一部分,a表示当前在a城市,(pos,a)作为节点。
由于联票只能从头开始并在后面的任意一个地方下车,因此想到从第一站依次向后面的站点连边。枚举到达第一个城市是已经经过了多少城市visited,然后顺着联票走,计算相应的pos,连边。
最后跑dij即可。记录一下方案。
Code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <map>
#include <stack>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
#define MP make_pair
using namespace std;
const int N = 1005;
const int M = 10005;
const int INF = 0x3f3f3f3f;
int NI,NT;
int ticketcost[N],ticketlen[N];
int listlen;
vector<int> list;
vector<int> ticket[N];
map<int,int> cityID;
int reID[N]; int IDcnt;
pair<pair<int,int>,int> pre[15][N];
int ID(int x){
if(!cityID.count(x)) cityID[x]=++IDcnt, reID[IDcnt]=x;
return cityID[x];
}
struct Edge{
int apos,a,bpos,b,next,w,id;
}e[M*2];
int head[15][N],ec=0;
void edgeClear(){ memset(head,0,sizeof(head)); ec=0; }
void add(int apos,int a,int bpos,int b,int w,int id){
//D(a); D(b); D(w); E;
ec++;
e[ec].apos=apos; e[ec].a=a;
e[ec].bpos=bpos; e[ec].b=b;
e[ec].w=w; e[ec].id=id;
e[ec].next=head[apos][a]; head[apos][a]=ec;
}
void clearAll(){
IDcnt=0; cityID.clear();
edgeClear();
}
struct Node{
int pos,a,val;
Node(int _p,int _a,int _v){
pos=_p; a=_a; val=_v;
}
bool operator < (const Node &tp) const {
return val > tp.val;
}
};
int d[15][N],done[15][N];
int dij(int S,int T){
memset(done,0,sizeof(done));
memset(d,0x3f,sizeof(d));
memset(pre,-1,sizeof(pre));
priority_queue<Node> q;
d[0][S]=0;
q.push(Node(0,S,0));
while(!q.empty()){
Node u=q.top(); q.pop();
//D(u.a); D(u.pos); D(u.val); E;
if(done[u.pos][u.a]) continue;
done[u.pos][u.a]=1;
for(int i=head[u.pos][u.a];i;i=e[i].next){
Node v=Node(e[i].bpos,e[i].b,u.val+e[i].w);
//cout<<" "; D(v.a); D(v.pos); D(v.val); E;
if(d[v.pos][v.a] > u.val+e[i].w){
d[v.pos][v.a]=u.val+e[i].w;
pre[v.pos][v.a]=MP(MP(u.pos,u.a),e[i].id);
q.push(v);
}
}
}
return d[listlen-1][list[list.size()-1]];
}
void init(){
edgeClear();
for(int tic=0;tic<NT;tic++){
for(int visited=0;visited<listlen-1;visited++){
int pos=visited;
for(int i=1;i<ticketlen[tic];i++){
if(ticket[tic][i]==list[pos+1]) pos++;
add(visited,ticket[tic][0],pos,ticket[tic][i],ticketcost[tic],tic);
}
}
}
}
int main(){
freopen("a.in","r",stdin);
int cas=0;
while(scanf("%d",&NT)==1 && NT){
clearAll(); ++cas;
for(int i=0;i<NT;i++){
scanf("%d%d",&ticketcost[i],&ticketlen[i]);
int x; ticket[i].clear();
for(int j=0;j<ticketlen[i];j++){
scanf("%d",&x); x=ID(x);
ticket[i].push_back(x);
}
}
scanf("%d",&NI);
for(int i=0;i<NI;i++){
scanf("%d",&listlen);
list.clear();
int x;
for(int j=0;j<listlen;j++){
scanf("%d",&x);
list.push_back(ID(x));
//list.push_back(x);
}
init();
int ans=dij(list[0],list[list.size()-1]);
printf("Case %d, Trip %d: Cost = %d\n",cas,i+1,ans);
printf(" Tickets used:");
int pos=listlen-1, u=list[list.size()-1];
stack<int> s;
while(pos>=0){
s.push(pre[pos][u].second+1);
pair<int,int> tp=pre[pos][u].first;
pos=tp.first; u=tp.second;
}
s.pop();
while(!s.empty()){
printf(" %d",s.top()); s.pop();
}
puts("");
}
}
}