此题使用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; }
#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; }
/** * 读取一个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; }
/*------------------------------------------------------------------- * 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; }