邻接链表
//sicily 1024. Magic Island
#include<iostream>
#include<cstring>
using namespace std;
struct Edge
{
int v,w;
int next;
}edge[20010];
int head[10010],curr,vis[10010],res;
void add_edge(int x,int y,int d) //读入的边插入到图的邻接链表
{
edge[curr].v=y;edge[curr].w=d;
edge[curr].next=head[x];
head[x]=curr;
curr++;
}
void dfs(int st,int len)
{
vis[st]=1;
int end=1;
for(int i=head[st];i!=-1;i=edge[i].next)
{
if(vis[edge[i].v]==0)
end=0,dfs(edge[i].v,len+edge[i].w);
}
if(end==1) //该路径深搜完毕
res=max(res,len);
vis[st]=0;
}
int main()
{
int n,k,x,y,d;
while(cin>>n>>k)
{
memset(head,-1,sizeof(head));
curr=0;
for(int i=1;i<n;++i)
{
cin>>x>>y>>d;
add_edge(x,y,d);
add_edge(y,x,d); //无向边
}
memset(vis,0,sizeof(vis));
res=0;
dfs(k,0);
cout<<res<<endl;
}
return 0;
}
用邻接链表实现Dijkstra,SPFA时有无重边并没有影响
//poj 3013 Big Christmas Tree
#include<iostream>
#include<deque>
using namespace std;
#define maxn 50002
const __int64 inf=(__int64)1<<63-1;
struct Edge
{
int v;
int weight;
int next;
}Vertex[4*maxn];
int head[maxn],curr;
int cases,v,e,i,j,edge[maxn][3],w[maxn];
void add_edge(int s,int t,int w) //新增结点不用申请空间, 跑了 579 MS
{
Vertex[curr].v=t;
Vertex[curr].weight=w;
Vertex[curr].next=head[s];
head[s]=curr++;
}
bool S[maxn];
__int64 distD[maxn],sum;
void spfa(int u)
{
fill(distD,distD+v+1,inf);
distD[u]=0;
memset(S,0,sizeof(S[0])*(v+1));
S[u]=1;
deque<int> col;
col.push_back(u);
while(!col.empty())
{
int uu=col.front();
col.pop_front();
S[uu]=0;
for(i=head[uu];i!=-1;i=Vertex[i].next)
{
if(distD[uu]+Vertex[i].weight<distD[Vertex[i].v])
{
distD[Vertex[i].v]=distD[uu]+Vertex[i].weight;
if(!S[Vertex[i].v])
{
S[Vertex[i].v]=1;
col.push_back(Vertex[i].v);
}
}
}
}
for(i=1;i<=v;++i)
if(distD[i]==inf)
{
printf("No Answer\n");
return ;
}
sum=0;
for(i=1;i<=v;++i)
sum+=w[i]*distD[i];
printf("%I64d\n",sum);
}
int main()
{
scanf("%d",&cases);
while(cases--)
{
scanf("%d%d",&v,&e);
for(i=1;i<=v;++i)
scanf("%d",&w[i]);
memset(head,-1,sizeof(head));
curr=0;
for(i=1;i<=e;++i)
{
scanf("%d%d%d",&edge[i][0],&edge[i][1],&edge[i][2]);
add_edge(edge[i][0],edge[i][1],edge[i][2]);
add_edge(edge[i][1],edge[i][0],edge[i][2]);
}
spfa(1);
}
return 0;
}
邻接链表实现单源最短路SPFA
/*
这题要有个重要的转化;
price of an edge will be (sum of weights of all descendant nodes) × (unit price of the edge).
每条边的代价为该边所有"后继"节点权值(重量weight)之和乘以该边的权值(unit price)。
换个角度就是每个节点的代价为该节点到根节点的所有边的权值乘以该节点的权值。
要使得总的花费最小,其实就是求从根结点到每个点的最短路径*该点的权值,然后求和
数据量很大,采用SPFA算法
*/
#include<iostream> //邻接链表实现单源最短路SPFA
#include<deque>
using namespace std;
#define maxn 50002
const __int64 inf=(__int64)1<<63-1; //inf=4611686018427387904
//(1)#define inf (1<<63)-1 (2)const __int64 inf=(__int64)(1<<63)-1; (3)const __int64 inf=(1<<63)-1; 这三者的值都为-1
struct Edge
{
int index;
int weight;
Edge *next;
}Vertex[maxn];
int cases,v,e,i,j,edge[maxn][3],w[maxn];
void add_edge(int s,int t,int w)
{
Edge *q=new Edge; //每次都new 耗时间,跑了2391 MS
q->index=t;q->weight=w;
q->next=Vertex[s].next;Vertex[s].next=q; //不必检查重边,直接插入即可
}
bool S[maxn];
__int64 distD[maxn],sum; //这里要声明为__int64
void spfa(int u)
{
fill(distD,distD+v+1,inf);
distD[u]=0;
memset(S,0,sizeof(S[0])*(v+1));
S[u]=1;
int uu,vv,wei;
deque<int> col; //spfa 队列
col.push_back(u);
while(!col.empty())
{
uu=col.front();
col.pop_front();
S[uu]=0;
Edge *curr=Vertex[uu].next;
while(curr!=NULL)
{
vv=curr->index;wei=curr->weight;
if(distD[uu]+wei<distD[vv])
{
distD[vv]=distD[uu]+wei;
if(!S[vv])
{
S[vv]=1;
col.push_back(vv);
}
}
curr=curr->next;
}
}
for(i=1;i<=v;++i)
if(distD[i]==inf)
{
printf("No Answer\n");
return ;
}
sum=0;
for(i=1;i<=v;++i)
sum+=w[i]*distD[i];
printf("%I64d\n",sum);
}
int main()
{
scanf("%d",&cases);
while(cases--)
{
scanf("%d%d",&v,&e);
for(i=1;i<=v;++i)
scanf("%d",&w[i]);
for(i=1;i<=v;++i)
Vertex[i].next=NULL;
for(i=1;i<=e;++i)
{
scanf("%d%d%d",&edge[i][0],&edge[i][1],&edge[i][2]);
add_edge(edge[i][0],edge[i][1],edge[i][2]); //双向边
add_edge(edge[i][1],edge[i][0],edge[i][2]);
}
spfa(1);
}
return 0;
}
/*
要注意以下情况:
(1)当v=0,1时都是输出0,但不必另外加判断,
因为当v=0时,在决定是否No Answer的for循环for(i=1;i<=v;++i)本身就不会执行,sum=0
当v=1时,生成树中的根结点1的distD[1]=0,其他结点(若有的话)的distD也为0,sum+=w[i]*distD[i];sum仍然为0
(2)也不必另外筛除掉指向自己的边,因为在SPFA函数内的if(distD[v]+w<distD[vv]) 显然distD[v]=distD[vv],所以不成立,自然地这条边就被筛掉了
(3)重边的处理--另外,如果是邻接链表的SPFA(这道题v很大,也只能用邻接链表了),也不必特地筛除重边,
在求最短路径时当某边有更小值时,if(distD[v]+w<distD[vv])就会成立,相应的边会被更新的,所以最终还是用到这条边的最小值
事实上,如果在构图时对于新加进的边,如果与之前的有重复,取较小值,同样是可以AC的,如下
void add_edge(int s,int t,int w)
{
Edge *q=new Edge;
q->index=t;q->weight=w;
Edge *tmp=Vertex[s].next;
while(tmp!=NULL&&tmp->index!=t)
tmp=tmp->next;
if(tmp==NULL)
{
q->next=Vertex[s].next;Vertex[s].next=q; //没找到重边
}
else
tmp->weight=min(tmp->weight,w); //找到重边,取较小值
}
当然,如果是邻接矩阵就要判断重边了,取其中的最小值
(4)边是无向边
(5)要注意inf的取值,最短路径数组distD和结果sum 都要用__int64 存储
另外,不用deque,也可用循环队列,如下:
void spfa(int u)
{
fill(distD,distD+v+1,inf);
distD[u]=0;
memset(S,0,sizeof(S[0])*(v+1));
S[u]=1;
int uu,vv,wei;
int deq[maxn],head=0,rear=1;
deq[0]=u;
while(head!=rear) //循环队列
{
uu=deq[head++];
if(head==maxn)
head=0;
S[uu]=0;
Edge *curr=Vertex[uu].next;
while(curr!=NULL)
{
vv=curr->index;wei=curr->weight;
if(distD[uu]+wei<distD[vv])
{
distD[vv]=distD[uu]+wei;
if(!S[vv])
{
S[vv]=1;
deq[rear++]=vv;
if(rear==maxn)
rear=0;
}
}
curr=curr->next;
}
}
for(i=1;i<=v;++i)
if(distD[i]==inf)
{
printf("No Answer\n");
return ;
}
sum=0;
for(i=1;i<=v;++i)
sum+=w[i]*distD[i];
printf("%I64d\n",sum);
}
*/