[editorial] Editorial - G. 성배

  • Toivoa
    Toivoa

    예상 외로 아무도 못 푼것을 보면 문제를 이해하기 쉽지 않았던 것이라고 생각됩니다. 문제 설명을 이해하기 어려워서인듯 한데 이게 다 제 작문 능력 탓입니다. ㅠㅠ
    해법
    1. 어떤 방에 도착했을 때 그 직전 방을 계속 왔다 갔다 하는 것이 가능합니다. 따라서 어떤 방에 처음 방문한 시간이 5라면 5, 7, 9, ... 등으로 5 이상의 홀수 시간에는 항상 그 방을 방문할 수 있습니다.
    2. 이 성질을 이용해서 어떤 방에 도달할 수 있는 최소의 시간을 짝수, 홀수로 나누어서 Dijkstra 등을 이용해서 구하면 순간이동을 할 수 있는 가장 빠른 시간을 알아낼 수 있습니다.
    3. 주의하실 점은 순간이동 해서 다른 방으로 이동할 때에도 짝수, 홀수 시간의 순간이동을 모두 계산해주어야 합니다. 이유는 조금만 생각해보시면 아실테니 생략합니다.
    4. 성배가 있는 방에서 돌아오는 경우도 마찬가지로 입구에서 성배가 있는 방에 도달할 수 있는 최소의 짝수, 홀수 시간을 구해놓고 입구로 돌아오는 시간을 계산하면 됩니다.
    소스코드

    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <vector>
    using namespace std;
    struct Teleport
    {
     bool hasTeleport;
     int  to;
     int  firstTime;
     int  interval;
     int  getNextOddTime(int t)  // t 시간 이후로 teleport가 되는 홀수 시간 (t 시간을 포함하지 않는다)
     {
      if (interval % 2 == 0 && firstTime % 2 == 0) return -1;
      int ret = -1;
      int x = t / interval;
      if (interval % 2 == 0)
      {
       ret = x * interval + firstTime;
       if (ret <= t) ret += interval;
      }
      else
      {
       if (x % 2 == firstTime % 2) ++x;
       ret = x * interval + firstTime;
       if (ret <= t) ret += interval * 2;
      }
      return ret;
     }
     int  getNextEvenTime(int t)
     {
      if (interval % 2 == 0 && firstTime % 2 == 1) return -1;
      int ret = -1;
      int x = t / interval;
      if (interval % 2 == 0)
      {
       ret = x * interval + firstTime;
       if (ret <= t) ret += interval;
      }
      else
      {
       if (x % 2 != firstTime % 2) ++x;
       ret = x * interval + firstTime;
       if (ret <= t) ret += interval * 2;
      }
      return ret;
     }
     bool isTeleportTime(int t)
     {
      return (t % interval == firstTime);
     }
    };
    struct Node
    {
     int visited[2];
     bool processed[2];
     vector<int> edge;
    };
    Node node[1000];
    Teleport tel[1000];
    int n, m, t;
    struct travel
    {
     int now;
     int time;
     bool operator < (const travel& rhs) const
     {
      return time > rhs.time;
     }
    };
    void go(int start)
    {
     priority_queue<travel> q;
     travel c, d;
     c.now = start;
     if (node[start].visited[0] != 2147483647)
     {
      c.time = node[start].visited[0];
      q.push(c);
     }
     if (node[start].visited[1] != 2147483647)
     {
      c.time = node[start].visited[1];
      q.push(c);
     }
     while (!q.empty())
     {
      c = q.top(); q.pop();
      if (node[c.now].processed[c.time % 2]) continue;
      if (node[c.now].edge.empty()) continue;
      node[c.now].processed[c.time % 2] = true;
      d.time = c.time + 1;
      for (size_t i = 0; i < node[c.now].edge.size(); ++i)
      {
       d.now = node[c.now].edge[i];
       if (tel[d.now].hasTeleport)
       {
        if (tel[d.now].isTeleportTime(d.time))
        {
         if (d.now == n - 1)
         { // 성배방인 경우 시간 - 2를 넣어서 d.time 시간에 성배방에서 바로 순간이동할 수 있게 한다.
          node[d.now].visited[d.time % 2] <?= d.time - 2;
         }
         d.now = tel[d.now].to;
        }
       }
       if (node[d.now].visited[d.time % 2] > d.time)
       {
        node[d.now].visited[d.time % 2] = d.time;
        q.push(d);
       }
      }
      if (tel[c.now].hasTeleport)
      {
       d.now = tel[c.now].to;
       if (c.time % 2 == 0)
        d.time = tel[c.now].getNextEvenTime(c.time);
       else
        d.time = tel[c.now].getNextOddTime(c.time);
       if (d.time != -1 && node[d.now].visited[d.time % 2] > d.time)
       {
        node[d.now].visited[d.time % 2] = d.time;
        q.push(d);
       }
      }
     }
    }
    int solve()
    {
     node[0].visited[0] = 0;
     go(0);
     if (node[n - 1].visited[0] != 2147483647 || node[n - 1].visited[1] != 2147483647)
     {
      for (int i = 0; i < n - 1; ++i)
      {
       node[i].visited[0] = node[i].visited[1] = 2147483647;
       node[i].processed[0] = node[i].processed[1] = false;
      }
      node[n - 1].processed[0] = node[n - 1].processed[1] = false;
      go(n - 1);
      int result = min(node[0].visited[0], node[0].visited[1]);
      if (result == 2147483647) return -1;
      return result;
     }
     return -1;
    }
    int main()
    {
     int T;
     scanf("%d", &T);
     while (T--)
     {
      scanf("%d%d%d", &n, &m, &t);
      for (int i = 0; i < n; ++i)
      {
       tel[i].hasTeleport = false;
       node[i].visited[0] = node[i].visited[1] = 2147483647;
       node[i].processed[0] = node[i].processed[1] = false;
       node[i].edge.clear();
       node[i].edge.reserve(1000);
      }
      for (int i = 0; i < m; ++i)
      {
       int x, y;
       scanf("%d%d", &x, &y);
       node[x].edge.push_back(y);
       node[y].edge.push_back(x);
      }
      for (int i = 0; i < t; ++i)
      {
       int x, y, q, w;
       scanf("%d%d%d%d", &x, &y, &q, &w);
       tel[x].hasTeleport = true;
       tel[x].to = y;
       tel[x].firstTime = q;
       tel[x].interval = w;
      }
      printf("%dn", solve());
     }
    }
    
    [이 글은 과거 홈페이지에서 이전된 글입니다. 원문보기]

    15년 전
4개의 댓글이 있습니다.
  • 설지
    설지

    어렵네요 ㅜㅜ


    15년 전 link
  • 하웨씨언
    하웨씨언

    102번째줄에서 에러가 나는데 어떤가요??


    15년 전 link
  • Toivoa
    Toivoa

    <?= 연산자가 gcc extension이라서 vc에서는 에러납니다.
    표준 스타일로 쓰면 node[d.now].visited[d.time % 2] = min(node[d.now].visited[d.time % 2], d.time - 2); 입니다.


    15년 전 link
  • 하웨씨언
    하웨씨언

    감사합니다 ^^


    15년 전 link
  • 정회원 권한이 있어야 커멘트를 다실 수 있습니다. 정회원이 되시려면 온라인 저지에서 5문제 이상을 푸시고, 가입 후 7일 이상이 지나셔야 합니다. 현재 문제를 푸셨습니다.