POJ2267 From Dusk till Dawn or: Vladimir the Vampire

 题目大意是一只吸血鬼(--!)要从一座城市抵达另一座城市,但是他只能乘坐火车,每个城市之间的火车都有一个到站时间和发车时间,既然是吸血鬼,那么每到中午12:00就要喝一公升的鲜血,吸血鬼只能乘坐区间段在0:00~6:00或者18:00~0:00之间的车(也就是说发车时间和抵达时间都要在这个区间内部)问从城市a->b吸血鬼最少要带多少升鲜血

这题有点难度,难点就在于图的建立,如果对点之间进行建图由于相关两点的只有一个权值而且这个权值的意义只是到站时间和出站时间,题目要求的是血数,那么就应该对边进行建图,先将不合要求的边去掉,然后记录下剩余边,再对边进行重新建图,如果从当前第i条边到第j条边需要消耗一升血,那么就在ij之间连一条边,权值为1,否则为0,这样SPFA一次可得结果,注意SPFA的时候要将起点所连接的边全部先入队、题目中的边为有向边、发车时间有可能大于24

#include<iostream>
#include<cstring>
#include<map>
#include<string>
#include<cstdio>
using namespace std;

using namespace std;
const int MAXN = 1100;
const int MAXM = MAXN*MAXN;
struct node
{
       int  v, next;
       int day;
}mapp[1000000];
struct edge
{
       int u, v;
       int stime, etime;
}ed[MAXM];
map <string, int> maze;
int head[MAXM];
int id;
void init()
{
     id = 0;
     memset(head, -1, sizeof(head));     
}

void addedge(int u, int v,int da)
{
     mapp[id].v = v, mapp[id].day = da, mapp[id].next = head[u], head[u] = id ++;
}
const int inf = 1 << 30;
int ans;
int Que[100*MAXM];
int dist[MAXM];
bool inque[MAXM];
void SPFA(int s, int e, int n)
{
     int front, rear;
     front = rear = 0;
     for (int i = 1; i < n; i ++){
         dist[i] = inf, inque[i] = false;
         if (ed[i].u == s){
            Que[rear ++] = i;
            inque[i] = true;
            dist[i] = 0;            
         }
     }
     while (front < rear ){
           int pre = Que[front ++];
           inque[pre] = false;
           for (int i = head[pre]; i != -1; i = mapp[i].next){
               int v = mapp[i].v;
               if (dist[v] > dist[pre]+mapp[i].day){
                   dist[v] = dist[pre]+mapp[i].day;
                   if (!inque[v]){
                      Que[rear ++] = v;
                      inque[v] = true;               
                   }
               }    
           }      
     }
     for (int i = 1; i < n; i ++){
         if (ans > dist[i] && ed[i].v == e){
            ans = dist[i];            
         }   
     }
}
int check(int i, int j)
{
    //这里有点纠结,要考虑需要消耗一升血的条件,当发车时间和到站时间都<=6或者都>=18时,显然当到站时间大于发车时间时,吸血鬼要等待一天,这样必然要消耗一升血
    //当到站时间<=6并且发车时间>=18时因为要经过12:00所以也要消耗一升血 
    if ((ed[j].stime < ed[i].etime&&((ed[j].stime <= 6 && ed[i].etime <= 6) || (ed[j].stime >= 18 && ed[i].etime >= 18))) || (ed[j].stime >= 18 && ed[i].etime <= 6))
       return 1;
    else return 0; 
}
int main()
{
    int t;
    scanf("%d", &t);
    int nc = 0;
    while (t --){
          int m;
          init();
          scanf("%d", &m);
          maze.clear();
          string a, b;
          int c, d;
          int len = 1;
          int num = 1;        
          while (m --){
                cin >> a >> b;
                scanf("%d%d", &c, &d);
                if (!maze[a]) 
                   maze[a] = num ++;
                if (!maze[b])
                   maze[b] = num ++;
                c%=24;
                if (c >= 18 && (c+d) <= 30 || (c <= 6 && (c+d) <= 6)){//当前时间为合法时间 
                   ed[len].u = maze[a], ed[len].v  = maze[b], ed[len].stime = c, ed[len ++].etime = (c+d)%24;
                }
          }
          
          cin >> a >> b;
          printf("Test Case %d.\n", ++ nc);
          if (maze[a] == maze[b]){
             printf("Vladimir needs 0 litre(s) of blood.\n");            
             continue;
          }
          if (maze[a] == 0 || maze[b] == 0){
             printf("There is no route Vladimir can take.\n");            
             continue;       
          }
          for (int i = 1; i < len; i ++){
              for (int j = 1; j < len; j ++){
                  if (ed[i].v == ed[j].u){//如果存在一条合法边 
                     int k = check(i, j);
                     addedge(i, j, k);
                  }    
              }
          } 
          ans = inf;
          SPFA(maze[a], maze[b], len); 
          if (ans == inf) printf("There is no route Vladimir can take.\n");
          else printf("Vladimir needs %d litre(s) of blood.\n", ans);
    }   
    return 0;    
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值