ZJU2008 Invitation Cards - 链表Dijkstra+堆

题目大意:

n个人最初都在1顶点,分别派去有向图中的其余n个顶点,然后所有人再回到出发点。要求所有人的最小费用。 n,m<1000000

分析:

可以看出,其实就是要求单源最短路径。分开两次求,把所有向边正向的图算一次,然后把边全部反过来,反向算一次即可。可以使用Dijkstra算法。由于数据量超大,所以只能用邻接表来存。

这里可以对普通Dijkstra进行改进。在每次确定当前顶点中的最近顶点时,我们使用,而不是顺序扫描。这样会快很多。

下面贴一下代码,在ZJU上用了1.41s

/*
ZJU2008 Invitation Cards
*/

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#define N 1000005
#define size sizeof(node)
#define newNode (node*)malloc(size)
#define clr(c) memset(c,0,sizeof(c))

struct linknode{
    int x,p;
    struct linknode *next;
};
typedef struct linknode node;

node *a[N],*b[N];
int m,n;
char e[N],u[N];
int v[N];
int d[N],dn;

void ins(node **a,int x,int p)
{
    node *t,*s;
    if(*a==0){
        *a=newNode;
        (*a)->x=x;
        (*a)->p=p;
        return;
    }
    s=newNode;
    s->x=x;
    s->p=p;
    t=(*a)->next;
    (*a)->next=s;
    s->next=t;
}

void heap(int p)
{
    int q,x=d[p];
    while(p<=dn/2)
    {
        q=p+p;
        if(q<dn&&v[d[q+1]]<v[d[q]]) q++;
        if(v[x]<=v[d[q]]) break;
        d[p]=d[q];
        p=q;
    }
    d[p]=x;
    return;
}

void reheap(int p)
{
    int q,x=d[p];
    while(p>1)
    {
        q=p/2;
        if(v[x]>=v[d[q]]) break;
        d[p]=d[q];
        p=q;
    }
    d[p]=x;
    return;
}

int dij(node *a[])
{
    int i,j,k,x,sum=0;
    node *p,*q;
    d[1]=1; u[1]=1;
    dn=1;
   
    for(i=0;i<n;i++)
    {
        //get the top
        x=d[1];
        e[x]=1;
        sum+=v[x];
        d[1]=d[dn--];
        heap(1);
       
        //printf("get %d: %d/n",x,v[x]);
        //add new
        p=a[x];
        while(p!=0){
            k=p->x;
            if(!e[k]){
                if(v[k]>v[x]+p->p||!u[k]){
                    v[k]=v[x]+p->p;
                }
                if(!u[k])
                {  
                    dn++;
                    //printf("add %d : %d/n",k,dn);
                    d[dn]=k;
                    u[k]=1;
                    reheap(dn);
                }
            }
            p=p->next;
        }
    }
    return sum;
}

int main()
{
    int i,j,k,T;
   
    scanf("%d",&T);
   
    while(T--)
    {
        int f,t,p,ans;
       
        //init
        clr(a); clr(b);
       
        //input
        scanf("%d%d",&n,&m);
        for(i=0;i<m;i++){
            scanf("%d%d%d",&f,&t,&p);
            ins(&a[f],t,p);
            ins(&b[t],f,p);
        }
       
        //dij
        clr(e); clr(u);
        clr(v); clr(d);
        ans=dij(a);
       
        clr(e); clr(u);
        clr(v); clr(d);
        ans+=dij(b);
       
        //output
        printf("%d/n",ans);
    }
   
    return 0;
}

 

/*

Sample Input

2
2 2
1 2 13
2 1 33
4 6
1 2 10
2 1 60
1 3 20
3 4 10
2 4 5
4 1 50


Sample Output

46
210


*/

你可能感兴趣的:(ZJU2008 Invitation Cards - 链表Dijkstra+堆)