关闭

POJ 3159 candies总结

1349人阅读 评论(0) 收藏 举报

此题使用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;
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:223035次
    • 积分:3659
    • 等级:
    • 排名:第8899名
    • 原创:89篇
    • 转载:9篇
    • 译文:0篇
    • 评论:62条
    最新评论