hdu-6166(最短路+二进制分组)

题意:给你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\n",cot,ans);
    }
}

  

,每一位分别跑两次最短路,一次是所有的0到所有的1,另一次是所有的1到所有的0,一共是四十次不到,如果根据每次给的n来跑,会更小。

ps:这里的最短路是多源多汇的;

代码:

posted @ 2018-08-15 23:47  荒岛的龟  阅读(527)  评论(0编辑  收藏  举报