http://acm.hdu.edu.cn/showproblem.php?pid=6166
给定一个有向图,再给你一个集合。问这些集合中 两个点之间相互的最短路距离最小是多少。
思路:集合中每个数二进制都有一位不相同,
所以通过枚举二进制可以保证 包含了所有 两两枚举的情况。
然后dij的思路 就是通过 贪心+集合+有限的松弛操作 来搞事情的。
所以也可以计算集合之间的最短路,(学了一招qwq)
(关于为啥这样是最短的,我感觉在这里就像理解一个广搜一样。。)
#include <bits/stdc++.h>
using namespace std;
/* dij在过程中,是通过贪心+松弛+集合 来判断的。
*/
typedef long long ll;
const int maxn=5e6+2000;
int m,n,k;
int a[maxn];
bool vis[maxn];
ll dis[maxn];
struct Edge{
int to,next,cost;
}edge[maxn];
int len;
int head[maxn];
void add(int a,int b,int c){
edge[len].to=b;
edge[len].cost=c;
edge[len].next=head[a];
head[a]=len++;
}
struct Node{
int to;
ll cost;
Node(){};
Node(int _a,ll _b){to=_a,cost=_b;};
friend bool operator < (Node a, Node b)
{
return a.cost>b.cost; //x小的优先级高。
}
};
priority_queue<Node>q;
ll dij(){
while(!q.empty()){
Node u=q.top();
q.pop();
if(vis[u.to])
return u.cost;
for(int i=head[u.to];i!=-1;i=edge[i].next){
int to=edge[i].to;
ll cos=1ll*edge[i].cost;
if(cos+u.cost<dis[to]){
dis[to]=cos+u.cost;
q.push(Node(to,dis[to]));
}
}
}
}
void INIT(){
for(int i=0;i<=m;i++)
dis[i]=1e16;
memset(vis,false,sizeof(vis));
while(!q.empty())
q.pop();
}
void init(){
memset(head,-1,sizeof(head));
len=0;
}
ll solve(){
ll ans=1e16;
for(int i=0;i<20;i++){
INIT();
for(int j=0;j<k;j++){
if((1<<i)&a[j]){
dis[a[j]]=0;
q.push(Node(a[j],0));
}
else
vis[a[j]]=true;
}
INIT();
for(int j=0;j<k;j++){
if(!((1<<i)&a[j])){
dis[a[j]]=0;
q.push(Node(a[j],0));
}
else
vis[a[j]]=true;
}
ans=min(ans,dij());
}
return ans;
}
int main(){
//freopen("F:\\ttt\\100dddd6.txt","r",stdin);
//freopen("F:\\ttt\\ou1.txt","w",stdout);
int t,a1,b1,c1;
scanf("%d",&t);
for(int tt=1;tt<=t;tt++){
init();
scanf("%d%d",&m,&n);
for(int i=0;i<n;i++){
scanf("%d%d%d",&a1,&b1,&c1);
add(a1,b1,c1);
}
scanf("%d",&k);
for(int i=0;i<k;i++){
scanf("%d",&a[i]);
}
printf("Case #%d: %lld\n",tt,solve());
}
return 0;
}