Low Cost Air Travel UVALive - 3561

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("");
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值