/* 题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3873 最短路的巧妙运用,本题较最短路问题多出了保护城市的条件,即一个城市x受若干城市保护,没有攻占所有保护x的“卫星城”就不可以攻占x dij过程中要维护其保护城市,用les[x]记录x所有“卫星城”被攻占的最早时间,那么能到x的最短时间为les[x]和到达x的最短路中的较大者 处理特点: dij入队过程中只把in[x](没有被包含的城市)入队 对于出对的x,它的最短时间已经确定,表示已经被占领,它所保护的城市的保护度减 1,一旦某个被保护的城市的保护度为零且已经到底(未占领,d[x]!=inf),就可以确定到达它的 最短时间(为max(les[x],d[x])),它也就到了入队的时机。 */ #include <cstdio> #include <iostream> #include <memory.h> #include<queue> using namespace std; const int MAXM=70009; const int MAXN=3009; const long long inf=(1LL)<<54; int n,m; struct Edge { int v,w,next; }edge[MAXM]; int first[MAXN],e,in[MAXN],vis[MAXN];//in[i] i的保护度(被几个城市保护) long long les[MAXN]; //les[i]表示所有保护i的点被摧毁的最早时间 long long d[MAXN]; //d[i]表示到底i的最短时间 vector<int> node[MAXN]; //node[i]中存放被i保护的节点 void add(int u,int v,int w) { edge[e].v=v;edge[e].w=w;edge[e].next=first[u]; first[u]=e++; } void init() { scanf("%d%d",&n,&m); int u,v,w,k; e=0; for(int i=1;i<=n;i++) { node[i].clear(); in[i]=0; vis[i]=0; d[i]=inf; first[i]=-1; les[i]=0; } for(int i=0;i<m;i++) { scanf("%d%d%d",&u,&v,&w); add(u,v,w); } for(int i=1;i<=n;i++) { scanf("%d",&in[i]); //点i的受保护数 for(int j=0;j<in[i];j++) { scanf("%d",&k); node[k].push_back(i);//i点压入k的保护之中 } } } typedef pair<long long,int> pii; long long dij(int s) { d[s]=0; priority_queue<pii,vector<pii>,greater<pii> > q; q.push(make_pair(d[s],s)); while(!q.empty()) { pii u=q.top();q.pop(); int x=u.second; if(vis[x])continue; vis[x]=1; int size=node[x].size(); for(int j=0;j<size;j++) //x已被摧毁,维护它包含的节点的信息 { int tmp=node[x][j]; in[tmp]--; //所有x包含的节点的入度减一 les[tmp]=max(les[tmp],d[x]); //更新所保护节点的最早脱离保护的时间 if(d[tmp]!=inf&&in[tmp]==0) //如果所包含点已经到达且脱离保护,到底其的最短时间为max(脱离保护时间,到达时间),并将其入队 { d[tmp]=max(d[tmp],les[tmp]); q.push(make_pair(d[tmp],tmp)); } } for(int k=first[x];k!=-1;k=edge[k].next) { int v=edge[k].v; if(d[v]>d[x]+edge[k].w) { d[v]=max(d[x]+edge[k].w,les[v]); if(in[v]==0) //只有这个点为无保护点才可以入队 { //d[v]=max(d[v],les[v]); q.push(make_pair(d[v],v)); } } } } return d[n]; } void solve() { dij(1); printf("%I64d\n",d[n]); } int main () { int Case; scanf("%d",&Case); while(Case--) { init(); solve(); } return 0; } /* 1 6 6 1 2 1 1 4 3 2 3 5 2 5 2 4 6 2 5 3 2 0 0 0 1 3 0 2 3 5 */