幻想历8012年5月12日深夜,斯普林·布拉泽降下神谕:“Trust me, earn eternal life.”克里斯军团士气大增。作为克里斯军团的主帅,你决定利用这一机会发动奇袭,一举击败杰森国。具体地说,杰森国有N个城市,由M条单向道路连接。神谕镇是城市1而杰森国的首都是城市N。你只需摧毁位于杰森国首都的曾·布拉泽大神殿,杰森国的信仰,军队还有一切就都会土崩瓦解,灰飞烟灭。
为了尽量减小己方的消耗,你决定使用自爆机器人完成这一任务。唯一的困难是,杰森国的一部分城市有结界保护,不破坏掉结界就无法进入城市。而每个城市的结界都是由分布在其他城市中的一些结界发生器维持的,如果想进入某个城市,你就必须破坏掉维持这个城市结界的所有结界发生器。
现在你有无限多的自爆机器人,一旦进入了某个城市,自爆机器人可以瞬间引爆,破坏一个目标(结界发生器,或是杰森国大神殿),当然机器人本身也会一起被破坏。你需要知道:摧毁杰森国所需的最短时间。
题目大意:求一条从1到n的最短路,有一个限制条件,每个节点都有几个前置点,只有访问所有前置点,才可以访问这个节点。
Solution
考虑每个访问一个点的最小代价是什么,就是他的所有前置点的最小代价和从1到这个点的最短路取最大值(可以想一想)。
这样,我们按照迪杰斯特拉的顺序,每取出一个点,更新最短路,并将自由点(前置点全部被访问的点)加入队列,再去将他的后置点入度建议,若出现入度为0,加入队列。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define N 3002 #define M 70002 #define mm make_pair using namespace std; priority_queue<pair<int,int> >q; int h[N],head[N],tot,tot1,n,m,dis[N],x,y,num[N],t[N]; bool vis[N]; struct edge{ int n,to,l; }e[M],ed[M]; inline void add(int u,int v,int l){ e[++tot].n=head[u]; e[tot].to=v; head[u]=tot; e[tot].l=l; } inline void add1(int u,int v){ ed[++tot1].n=h[u]; ed[tot1].to=v; h[u]=tot1; } int main(){ scanf("%d%d",&n,&m);int u,v,w; for(int i=1;i<=m;++i)scanf("%d%d%d",&u,&v,&w),add(u,v,w); memset(dis,0x3f,sizeof(dis)); for(int i=1;i<=n;++i){ scanf("%d",&x); for(int j=1;j<=x;++j){ scanf("%d",&y); num[i]++; add1(y,i); } } dis[1]=0;q.push(mm(0,1)); while(!q.empty()){ int u=q.top().second;q.pop(); if(vis[u])continue;vis[u]=1; dis[u]=max(dis[u],t[u]); for(int i=h[u];i;i=ed[i].n){ num[ed[i].to]--,t[ed[i].to]=max(t[ed[i].to],dis[u]); if(!num[ed[i].to])dis[ed[i].to]=max(dis[ed[i].to],t[ed[i].to]),q.push(mm(-dis[ed[i].to],ed[i].to)); } for(int i=head[u];i;i=e[i].n){ int v=e[i].to; if(dis[v]>dis[u]+e[i].l){ dis[v]=dis[u]+e[i].l; if(!num[v])q.push(mm(-dis[v],v)); } } } printf("%d ",dis[n]); return 0;; }