此题使用BellmanFord算法效果不佳,POJ提示超时,下面有一个算法使用dijkstra算法速度较快
/*-------------------------------------------------------------------
* Purpose:
* POJ 3159 candies 做了一些优化
* Time:
* 2012年3月21日 9:27:16
* Author:
* 张彦升
-------------------------------------------------------------------*/
#include <iostream>
using namespace std;
const int INF = 0x3f3f3f3f;
const int V = 30001;
const int E = 150001;
int pnt[E],cost[E],nxt[E];
int e,head[V];
int dist[V];
bool vis[V];
int relax(int u,int v,int c)
{
if (dist[v] > dist[u] + c)
{
dist[v] = dist[u] + c;
return 1;
}
return 0;
}
void addedge(int u,int v,int c)
{
pnt[e] = v;
cost[e] = c;
nxt[e] = head[u];
head[u] = e++;
}
int SPFA(int src,int n)
{
int i;
for (i = 1;i <= n;i ++)
{
vis[i] = 0;
dist[i] = INF;
}
dist[src] = 0;
int Q[E],top = 1;
Q[0] = src;
vis[src] = true;
while (top)
{
int u,v;
u = Q[--top];
vis[u] = false;
for (i = head[u];i != -1;i = nxt[i])
{
v = pnt[i];
if (1 == relax(u,v,cost[i]) && !vis[v])
{
Q[top++] = v;
vis[v] = true;
}
}
}
return dist[n];
}
int main()
{
int n,m;
while (scanf("%d%d",&n,&m) != EOF)
{
int i,a,b,c;
e = 0;
memset(head,-1,sizeof(head));
for (i = 0;i < m;++i)
{
cin >> a >> b >> c;
addedge(a,b,c);
}
cout << SPFA(1,n) << endl;
}
return 0;
}
使用dijkstra算法实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
//邻接表+优先级队列+dijkstra
const int maxn=160000;
const int inf=(1<<27);
struct edge
{
int t,w;//s->t=w;
int next;//s->的下一个定点
};
int p[maxn];//表头结点 初值-1
edge G[maxn];//邻接表
int l;//邻接表 初值0
int V,E;//点数 边数
//添加边
void addedge(int u,int v,int w)
{
G[l].t=v;
G[l].w=w;
G[l].next=p[u];
p[u]=l++;
}
//计算从s0到其他点的最短距离
struct CNode
{
int k,w;//s0->k=w;
};
bool operator < ( const CNode & d1, const CNode & d2 ) {
return d1.w > d2.w; //priority_queue总是将最大的元素出列
}
priority_queue<CNode> q;
bool vis[maxn];
int dis[maxn];//s0到其他点的最短距离
CNode tmp;//temp
void priority_queue_dijkstra(int s0)
{
memset(vis,0,sizeof(vis));
memset(dis,-1,sizeof(dis));
tmp.k=s0,tmp.w=0;
q.push(tmp);
while(!q.empty())
{
tmp=q.top();q.pop();
if(vis[tmp.k]) continue;
vis[tmp.k]=true;
dis[tmp.k]=tmp.w;
for(int i=p[tmp.k];i!=-1;i=G[i].next)
{
CNode t;
t.k=G[i].t;
if(vis[t.k]) continue;
t.w=tmp.w+G[i].w;
q.push(t);
}
}
}
int main()
{
while(scanf("%d%d",&V,&E)==2)
{
memset(p,-1,sizeof(p));//important
l=0;//important
for(int i=0;i<E;i++)
{
int u,v,w;scanf("%d%d%d",&u,&v,&w);//从1开始
//u->v=w
addedge(u,v,w);
}
priority_queue_dijkstra(1);
printf("%d\n",dis[V - 2]);
}
return 0;
}
下面版本是由我写的,没有做任何优化
/*-------------------------------------------------------------------
* Purpose:
* Candies Bellman Ford
* 为了竞赛,必须适应不用类封装而使用全局数据
* Time:
* 2012年3月21日 18:46:30
* Author:
* 张彦升
-------------------------------------------------------------------*/
#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
using namespace std;
/*我们需要顶点信息和边信息*/
struct Edge
{
int u;
int v;
int w;
Edge(int t_u,int t_v,int t_w)
:u(t_u),
v(t_v),
w(t_w)
{
return;
}
};
typedef vector<Edge> VecEdge;
typedef vector<int> VecInt;
class Candies
{
public:
/**
*
**/
Candies(int t_n,int t_m)
:n(t_n),
m(t_m),
dist(n,inf)
{
dist[0] = 0;
return;
}
/**
*
**/
~Candies()
{
return;
}
/**
* 添加一条新边
*/
int addedge(int u,int v,int w)
{
edge.push_back(Edge(u,v,w));
return 0;
}
/**
* bellman ford algorithm
*/
int bellman_ford()
{
/*对于每个节点松弛一次所有边*/
for (int i = 0;i < n;i ++)
{
relax_edge();
}
/*如果一切正常,返回到此点的最短路径*/
if (is_relax_ok() == true)
{
return 1;
}
return -1;
}
/**
* 返回路径
*/
int get_dis(int pos)
{
return dist[pos];
}
/**
* 松弛每条边
*/
int relax_edge()
{
VecEdge::iterator iter = edge.begin();
int ret = 1;
for (;iter != edge.end();++iter)
{
if (0 == relax(*iter))
{
ret = 0;
}
}
return ret;
}
/**
* 松弛改变
*/
int relax(Edge ed)
{
int v = ed.v - 1,u = ed.u - 1,w = ed.w;
if (dist[v] > dist[u] + w)
{
dist[v] = dist[u] + w;
return 1;
}
return 0;
}
/**
* 对负权回路进行检查
*/
bool is_relax_ok()
{
if (relax_edge() == 1)
{
return false;
}
return true;
}
protected:
VecEdge edge; /*记录所有的边信息*/
int n,m; /*n is kids number,m is cases number*/
VecInt dist;
static const int inf = 0x3f3f3f3f;
private:
};
/**
* 此题中我们不需要保存前点的信息
*/
int main()
{
/*n child and m cases*/
int n,m;
//fstream cin("data.txt",ios_base::in);
while (cin >> n >> m)
{
Candies candies(n,m);
int u,v,w; /*w = (u,v)*/
for (int i = 0;i < m;i++)
{
cin >> u >> v >> w;
candies.addedge(u,v,w);
}
/*加入所有边之后开始执行BellmanFord算法*/
/*计算得出结果*/
if (candies.bellman_ford() == -1)
{
cout << "此图存在错误" << endl;
}
cout << candies.get_dis(n - 1) << endl;
/*
for (int i = 0;i < n;i++)
{
cout << candies.get_dis(i) << endl;
}
*/
}
return 0;
}
上面的算法基本上都会出现TLE,使用栈要比队列好,具体为什么可能是测试数据的原因,使用队列的时候要对堆进行push_heap和pop_heap,这儿pop_heap实际上可以省略,那么将意味着效果更好,但是对SPFA优先队列无用,但此方案对Dijkstra有用,而此题目的意思是使用SPFA解决(因为其中有负边),虽然我写的Dijkstra算法也提交成功了,但我认为使用Dijkstra是不对的,使用map对查找最小值有一定的优化,或者我们应该考虑使用斐波那契堆,下面是对Dijkstra算法的一个优化版本
/**
* v_size表示节点的个数,src表示起始节点
*/
int dijkstra(int v_size,int src)
{
typedef pair<int,int> Node;
typedef multimap<int,int,less<int> > DijHeap;
DijHeap heap;
heap.insert(make_pair(0,src));
dis[src] = 0;
register int u,v,w,j;
Edge ed;
DijHeap::iterator iter,temp;
/*循环对没一个顶点访问*/
for (int i = 0; i < v_size;i++)
{
iter = heap.begin();
j = i;
while (j-- > 0)
{
++iter;
}
u = iter->second;
if (vis[u] == 1)
{
continue;
}
else
{
vis[u] = 1;
}
for (j = head[u];j != -1;j = ed.next)
{
ed = edge[j];
v = ed.v;
w = ed.w;
if (vis[v] == 0 && dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
heap.insert(make_pair(dis[v],v));
}
}
}
return 0;
}
最后不得不提的是scanf的读取问题,scanf读取整数的时候速度会很慢,使用下面实现的读取整数的函数会极大提升程序执行速度
/**
* 读取一个int
*/
inline int read_int()
{
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
最终得如下程序,经测试,是POJ3159目前发现最快的一个程序47MS
/*-------------------------------------------------------------------
* Purpose:
* POJ 3159 candies 做了一些优化
* Time:
* 2012年3月21日 9:27:16
* Author:
* 张彦升
-------------------------------------------------------------------*/
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int V = 30001;
const int E = 150001;
int pnt[E],cost[E],nxt[E];
int e,head[V];
int dist[V];
bool vis[V];
int relax(int u,int v,int c)
{
if (dist[v] > dist[u] + c)
{
dist[v] = dist[u] + c;
return 1;
}
return 0;
}
/**
* 读取一个int
*/
inline int read_int()
{
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
void addedge(int u,int v,int c)
{
pnt[e] = v;
cost[e] = c;
nxt[e] = head[u];
head[u] = e++;
}
int SPFA(int src,int n)
{
int i;
for (i = 1;i <= n;i ++)
{
vis[i] = 0;
dist[i] = INF;
}
dist[src] = 0;
int Q[E],top = 1;
Q[0] = src;
vis[src] = true;
while (top)
{
int u,v;
u = Q[--top];
vis[u] = false;
for (i = head[u];i != -1;i = nxt[i])
{
v = pnt[i];
if (1 == relax(u,v,cost[i]) && !vis[v])
{
Q[top++] = v;
vis[v] = true;
}
}
}
return dist[n];
}
int main()
{
int n,m;
while (scanf("%d%d",&n,&m) != EOF)
{
int i,a,b,c;
e = 0;
memset(head,-1,sizeof(head));
for (i = 0;i < m;++i)
{
//cin >> a >> b >> c;
a = read_int();
b = read_int();
c = read_int();
addedge(a,b,c);
}
cout << SPFA(1,n) << endl;
}
return 0;
}