题意:给你n个点m条边的有向图,然后再给你k个不同的点,问你这k个点的最小距离;
解题思路:这道题最需要注意的就是k个点一定是不同的,那么有一个结论就是任意两个不同的数字中,在他们的二进制地表示中,一定有一位是不同的,这样,我们就可以按照这个规律,把这些数字分成两组,按他们的二进制在某一位是0或者1分组,然后对每一位都跑一次最短路,这里的数据的二进制不超过20位
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #define maxn 100500 #define inf 0x3f3f3f3f using namespace std; struct node { int num; int dist; node(int _num=0,int _dist=0):num(_num),dist(_dist){} friend bool operator<(node a,node b) { return a.dist>b.dist; } }; struct Edge { int next; int to; int fa; int w; }edge[maxn]; int head[maxn]; int dist[maxn]; int cnt; int visit[maxn]; int flag[maxn]; int n,m; int k,x,y,w,t; int pow(int u) { int x=1; for(int i=1;i<=u;i++) x=x*2; return x; } void add(int u,int v,int w) { edge[cnt].next=head[u];edge[cnt].w=w; edge[cnt].to=v;edge[cnt].fa=u;head[u]=cnt++; } void dij() { memset(dist,inf,sizeof(dist)); priority_queue<node>q; for(int i=1;i<=n;i++) { if(flag[i]==1) { q.push(node(i,0));dist[i]=0; } } while(!q.empty()) { node now=q.top();q.pop(); x=now.num; for(int i=head[x];i!=-1;i=edge[i].next) { int v=edge[i].to; if(dist[v]>dist[x]+edge[i].w) { dist[v]=edge[i].w+dist[x]; q.push(node(v,dist[v])); } } } } int main() { int tt; int temp; int cot=0; scanf("%d",&tt); while(tt--) { cot++; //memset(flag,0,sizeof(flag)); memset(visit,0,sizeof(visit)); memset(head,-1,sizeof(head)); cnt=0; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&w); add(x,y,w); } scanf("%d",&k); for(int i=1;i<=k;i++) { scanf("%d",&t); visit[t]=1; } int ans=inf; for(int i=0;i<20;i++) { for(int j=1;j<=n;j++) { if(!visit[j]) continue; temp=pow(i);flag[j]=0; if((j&temp)==0) { flag[j]=1; } else flag[j]=-1; } dij(); for(int j=1;j<=n;j++) { if(!visit[j]) continue; if(flag[j]==-1) ans=min(ans,dist[j]); } for(int j=1;j<=n;j++) { if(!visit[j]) continue; temp=pow(i);flag[j]=0; if((j&temp)==0) { flag[j]=-1; } else flag[j]=1; } dij(); for(int j=1;j<=n;j++) { if(!visit[j]) continue; if(flag[j]==-1) ans=min(ans,dist[j]); } } printf("Case #%d: %d ",cot,ans); } }
,每一位分别跑两次最短路,一次是所有的0到所有的1,另一次是所有的1到所有的0,一共是四十次不到,如果根据每次给的n来跑,会更小。
ps:这里的最短路是多源多汇的;
代码: