题目链接:https://vjudge.net/problem/UVA-1048

题意:给定一些联票,在给定一些行程,要求这些行程的最小代价

解法:


#include <bits/stdc++.h>
using namespace std;
const int Max_City = 200;
const int Max_Ticket = 20;
const int Max_TicketLength = 10;
const int Max_TripLength = 10;
struct Info{
    int cost,n;
    int city[Max_TicketLength];
};

int N, n, q, trip, sum;//路线编号为trip,sum为总的城市数,n为某条旅行路线的长度
int city[Max_City];//城市序列
int List[Max_TripLength];//当前最小旅游费用路线上第i个到达的城市为List[i]
Info ticket[Max_Ticket];
int f[Max_TripLength][Max_City][4];//途径当前路线的前n个城市,
//最后到达城市m时,最小花费为f[n][m][0]
//最后使用的机票编号为f[n][m][1],
//使用本机票前途径的城市数为f[n][m][2],开始使用本机票的城市为f[n][m][3]
int route[Max_TicketLength*Max_City];//当期路线使用的机票序列
int Find(int x){
    int i;
    for(i=0;i<sum;i++) if(city[i]==x) return i;
    city[sum]=x;
    sum++;
    return (sum-1);
}
int main(){
    int ks = 0;
    while(scanf("%d", &N)!=EOF){
        if(N==0) break;
        sum=0;
        int tmp;
        for(int i=0; i<N; i++){
            scanf("%d %d", &ticket[i].cost,&ticket[i].n);
            for(int j=0; j<ticket[i].n; j++){
                scanf("%d", &tmp);
                ticket[i].city[j] = Find(tmp);
            }
        }
        ++ks;
        scanf("%d", &q);
        for(trip=0; trip<q; trip++){
            scanf("%d", &n);
            for(int i=0; i<n; i++){
                scanf("%d", &tmp);
                List[i] = Find(tmp);
            }
            //init
            for(int i=0; i<n; i++)
                for(int j=0; j<sum; j++)
                    f[i][j][0] = -1;
            for(int i=0; i<4; i++)
                f[0][List[0]][i] = 0;
            //递推路线上到达的城市数
            for(int i=0; i<n; i++){
                while(1){
                    bool change = 0;//更新标志初始化
                    for(int j=0; j<sum; j++)//枚举旅行者目前在的城市
                        if(f[i][j][0]>=0)//若已经求出到城市j的最小花费
                            for(int k=0; k<N; k++)//枚举在城市j开始换用的机票k
                            if(ticket[k].city[0]==j){
                                tmp=i+1;
                                //从当前路线的第i+1个城市出发,枚举使用优惠机票k可到达路线上的每个城市
                                for(int l=1;l<ticket[k].n; l++){
                                    if(tmp<n&&List[tmp]==ticket[k].city[l]) tmp++;
                                    if(f[tmp-1][ticket[k].city[l]][0]<0||f[tmp-1][ticket[k].city[l]][0]>f[i][j][0]+ticket[k].cost){
                                        f[tmp-1][ticket[k].city[l]][0] = f[i][j][0]+ticket[k].cost;
                                        f[tmp-1][ticket[k].city[l]][1] = k;
                                        f[tmp-1][ticket[k].city[l]][2] = i;
                                        f[tmp-1][ticket[k].city[l]][3] = j;
                                        change=1;
                                    }
                                }
                            }
                    if(change==0) break;
                }
            }
            printf("Case %d, Trip %d: Cost = %d\n", ks, trip+1, f[n-1][List[n-1]][0]);
            int l=0;
            int k,i=n-1,j=List[n-1];
            while(i||j!=List[0]){
                route[l]= f[i][j][1];//记下当前的机票编号
                k = f[i][j][2];//记下使用本机票前到过的城市数
                j = f[i][j][3];//记下使用本机票前旅行者在的城市
                i = k;
                l++;
            }
            printf("  Tickets used:");
            for(i=l-1; i>=0; i--){
                printf(" %d", route[i]+1);
            }
            printf("\n");
        }
    }
}