题目
1 5 6 1 2 1 2 3 3 3 1 3 2 5 1 2 4 2 4 3 1 3 1 3 5
Case #1: 2
题目大意
求给出的点之间形成的点对里面的最短路
解题思路
要是一遍一遍的去跑最短路,一定会超时,这里用的是求两个集合间的最短路来实现,把给我们的点集分成两个集合,这里用的是二进制来判断的,通过判断该数字是否在某一位上是不是1,是1加入一个集合,是0加入另一个集合,因为m<100000,化成二进制最多20位(估算的,没准确计算,1024*1024,1024是十位),这样最多20次,就一定可以保证每两个点都分在了不同的两个集合,然后求两个集合的最短路就可以了
求两个集合的最短路只要开两个超源就可以了,开一个0是一个集合的元素到0都为0,开一个超源n+1是另一个集合到n+1的距离都为0,这样子求0-n+1的最短路就是求两个集合之间的最短路径了
代码
#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3fLL
const int maxn=100000+10;
int x[maxn] , y[maxn] , val[maxn] , a[maxn] , g[maxn] , vis[maxn] ;
LL d[maxn] ;
int case_=1 , cnt ;
struct node
{
int x ;
LL v ;
bool operator < ( const node &b ) const ///优先队列改写小顶堆
{
return v > b.v ;
}
};
struct edge
{
int x ;
int to ;
LL v ;
}e[maxn<<1];
void addedge(int u,int v,int val)
{
e[++cnt] = (edge){v , g[u] , val} ;
g[u] = cnt ;///用链式前向星来记录
}
LL dijk(int u, int v)
{
priority_queueq;
for(int i = 0 ; i <= v ;i ++)
d[i] = INF , vis[i] = 0 ;
d[u] = 0 ;
//node tmp = (node){ u , 0 } ;
q.push((node){ u , 0 }) ;
while(!q.empty())
{
node t = q.top() ;
q.pop() ;
if(vis[t.x])
continue;
vis[t.x] = 1;
for(int i = g[t.x] ; ~i ; i = e[i].to)///~i i!=-1
{
if(!vis[e[i].x] && d[e[i].x] > d[t.x] + e[i].v)
{
//vis[e[i].x]=1;
d[e[i].x] = d[t.x] + e[i].v ;
q.push((node){e[i].x , d[e[i].x]}) ;
}
}
//vis[t.x]=0;
}
return d[v] ;
}
int main()
{
int T ;
scanf("%d", &T) ;
while(T--)
{
int n , m , k ;
LL ans = INF ;
scanf("%d%d" , &n , &m) ;
for(int i = 1 ; i <= m ; i++ ) scanf("%d%d%d",&x[i] , &y[i] , &val[i]) ;
scanf("%d" , &k) ;
for(int i = 1 ; i <= k ; i++) scanf("%d" , &a[i]) ;
for(int t = 0 ; t < 20 ; t++)
{
cnt = 0 ;
memset(g , -1 , sizeof(g)) ;
for(int i = 1 ; i <= m ; i++)
{
addedge(x[i] , y[i] , val[i]);
}
for(int i = 1 ; i <= k ; i++)
{
if(a[i] & (1 << t))///分成两个集合,一个集合开一个超源0,使该集合到0距离为0
addedge(0 , a[i] , 0) ;
else ///另一个集合开一个超源n+1,是该集合到n+1的距离为0,这样就可以求两个集合间的最短路径了
addedge(a[i] , n + 1 ,0) ;
}
ans = min(ans , dijk(0 , n + 1)) ;
// cout << "ans : " << ans << endl ;
cnt = 0 ; ///两个集合交换位置
memset(g , -1 , sizeof(g)) ;
for(int i = 1 ; i <= m ; i++)
{
addedge(x[i] , y[i] , val[i]) ;
}
for(int i = 1 ; i <= k ; i++)
{
if((a[i] & (1 << t)) == 0)///逻辑且优先级低,要加括号
addedge(0 , a[i] , 0) ;
else
addedge(a[i] , n + 1 ,0) ;
}
ans = min(ans , dijk(0 , n+1)) ;
// cout << "ans : " << ans << endl ;
}
printf("Case #%d: %lld\n",case_++ , ans) ;
}
return 0;
}