예상 외로 아무도 못 푼것을 보면 문제를 이해하기 쉽지 않았던 것이라고 생각됩니다. 문제 설명을 이해하기 어려워서인듯 한데 이게 다 제 작문 능력 탓입니다. ㅠㅠ
해법
1. 어떤 방에 도착했을 때 그 직전 방을 계속 왔다 갔다 하는 것이 가능합니다. 따라서 어떤 방에 처음 방문한 시간이 5라면 5, 7, 9, ... 등으로 5 이상의 홀수 시간에는 항상 그 방을 방문할 수 있습니다.
2. 이 성질을 이용해서 어떤 방에 도달할 수 있는 최소의 시간을 짝수, 홀수로 나누어서 Dijkstra 등을 이용해서 구하면 순간이동을 할 수 있는 가장 빠른 시간을 알아낼 수 있습니다.
3. 주의하실 점은 순간이동 해서 다른 방으로 이동할 때에도 짝수, 홀수 시간의 순간이동을 모두 계산해주어야 합니다. 이유는 조금만 생각해보시면 아실테니 생략합니다.
4. 성배가 있는 방에서 돌아오는 경우도 마찬가지로 입구에서 성배가 있는 방에 도달할 수 있는 최소의 짝수, 홀수 시간을 구해놓고 입구로 돌아오는 시간을 계산하면 됩니다.
소스코드
#include <cstdio>#include <algorithm>#include <queue>#include <vector>usingnamespacestd;structTeleport{boolhasTeleport;intto;intfirstTime;intinterval;intgetNextOddTime(intt)// t 시간 이후로 teleport가 되는 홀수 시간 (t 시간을 포함하지 않는다){if(interval%2==0&&firstTime%2==0)return-1;intret=-1;intx=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;}returnret;}intgetNextEvenTime(intt){if(interval%2==0&&firstTime%2==1)return-1;intret=-1;intx=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;}returnret;}boolisTeleportTime(intt){return(t%interval==firstTime);}};structNode{intvisited[2];boolprocessed[2];vector<int>edge;};Nodenode[1000];Teleporttel[1000];intn,m,t;structtravel{intnow;inttime;booloperator<(consttravel&rhs)const{returntime>rhs.time;}};voidgo(intstart){priority_queue<travel>q;travelc,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_ti=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);elsed.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);}}}}intsolve(){node[0].visited[0]=0;go(0);if(node[n-1].visited[0]!=2147483647||node[n-1].visited[1]!=2147483647){for(inti=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);intresult=min(node[0].visited[0],node[0].visited[1]);if(result==2147483647)return-1;returnresult;}return-1;}intmain(){intT;scanf("%d",&T);while(T--){scanf("%d%d%d",&n,&m,&t);for(inti=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(inti=0;i<m;++i){intx,y;scanf("%d%d",&x,&y);node[x].edge.push_back(y);node[y].edge.push_back(x);}for(inti=0;i<t;++i){intx,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());}}
Toivoa
예상 외로 아무도 못 푼것을 보면 문제를 이해하기 쉽지 않았던 것이라고 생각됩니다. 문제 설명을 이해하기 어려워서인듯 한데 이게 다 제 작문 능력 탓입니다. ㅠㅠ
해법
1. 어떤 방에 도착했을 때 그 직전 방을 계속 왔다 갔다 하는 것이 가능합니다. 따라서 어떤 방에 처음 방문한 시간이 5라면 5, 7, 9, ... 등으로 5 이상의 홀수 시간에는 항상 그 방을 방문할 수 있습니다.
2. 이 성질을 이용해서 어떤 방에 도달할 수 있는 최소의 시간을 짝수, 홀수로 나누어서 Dijkstra 등을 이용해서 구하면 순간이동을 할 수 있는 가장 빠른 시간을 알아낼 수 있습니다.
3. 주의하실 점은 순간이동 해서 다른 방으로 이동할 때에도 짝수, 홀수 시간의 순간이동을 모두 계산해주어야 합니다. 이유는 조금만 생각해보시면 아실테니 생략합니다.
4. 성배가 있는 방에서 돌아오는 경우도 마찬가지로 입구에서 성배가 있는 방에 도달할 수 있는 최소의 짝수, 홀수 시간을 구해놓고 입구로 돌아오는 시간을 계산하면 됩니다.
소스코드
16년 전