poj 3249 Test for Job 最长路

来源:互联网 发布:旅游市场需求状况数据 编辑:程序博客网 时间:2024/06/15 08:01

题意:给出一个有向无环图(DAG),可以不强连通,每个点有点权(正负)。

            现在有从每个入度为0的点到它可达的出度为0的点的所有路径,求每条路径上所有点权之和的最大值。

分析:其实就是求一条最长路。

做法:

  1. 很容易想到将spfa的松弛过程改为更新最大值
  2. 记忆化搜索,取当前节点所有后继的最大值更新自己
  3. 拓扑排序,然后dp递推过去,是2的逆过程
坑点:数据量很大是很大,给了5秒。但主要是有卡的数据,方法1弄不好就会TLE。在此题的Discuss里面,boys指出了“最坏的情况下,n次spfa,也就是说O(n^2),绝对超时。例如:10000个点(入度为0)都指向一条长度为90000的链的首端,spfa做,需要9*10^8次。” 可以仔细琢磨一下。因为spfa太不稳定,所以会出现各种状况。由此,我们可以考虑反向建图,飘过这样的数据。要是说反过来还有这样的数据呢,那这种方法就放弃吧…

分享:分享三份代码,与大家共同学习。

法1:反向建图,spfa变形。复杂度O(kE),k是一个不知道多大的常数(据说平均是2)。

#include <iostream>#include <cstdio>#include <cstring>#include <vector>#include <queue>using namespace std;#define rep(i,n) for(int i=0;i<n;i++)#define rep1(i,n) for(int i=1;i<=n;i++)#define LL long long#define N 100001 //顶点数#define M 1000001 //边数//poj3249 DAG 最长路?const LL INF = 1LL<<60;struct edge {    int y, next;};int V, E; //顶点数,边数LL d[N]; //最长距离int q[M], head, tail; //队列,头,尾int done[N]; //是否在队列里edge e[M]; //E条边int next[N], el; //i的下一条边的位置,记录边数int val[N]; //节点值int cnti[N]; //是否有入度void add(int x, int y) { //加边    e[++el].y = y;    e[el].next = next[x];    next[x] = el;}void spfa() {    head = tail = 0;    rep1(i,V) d[i] = -INF, done[i] = 0, q[i] = 0;    rep1(s,V) if(cnti[s]==0) {        d[s] = val[s];        done[s] = 1;        q[tail++] = s;    }    while(head < tail) {        int x = q[head++];        done[x] = 0;        for(int i=next[x]; i; i=e[i].next) {            int y = e[i].y;            LL tmp = d[x]+(LL)val[y];            if(d[y] < tmp) { //松弛操作                d[y] = tmp;                if(!done[y]) { //不在队列,入队                    q[tail++] = y;                    done[y] = 1;                }            }        }    }}int main() {    while(scanf("%d%d", &V,&E) != EOF) {        el = 0;        rep1(i,V) next[i] = cnti[i] = 0;        rep1(i,V) scanf("%d", val+i);        for(int i=0, x,y; i<E; i++) {            scanf("%d%d", &x,&y);            add(y,x); //反向建图            cnti[x]=1;        }        spfa();        LL ans = -INF;        rep1(i,V) {            if(next[i]==0 && ans < d[i]) {                ans = d[i];            }        }        printf("%I64d\n", ans);    }    return 0;}

法2:dfs记搜,复杂度O(V+E)

#include <iostream>#include <cstdio>#include <cstring>#include <vector>#include <queue>using namespace std;#define rep(i,n) for(int i=0;i<n;i++)#define rep1(i,n) for(int i=1;i<=n;i++)#define LL long long#define N 100001 //顶点数#define M 1000001 //边数//poj3249 DAG dfsconst LL INF = 1LL<<60;struct edge{    int y, next;};int V, E; //顶点数,边数LL d[N]; //最长距离edge e[M]; //E条边int next[N], el; //i的下一条边的位置,记录边数int val[N]; //节点值int cnti[N], cnto[N]; //入度,出度inline void add(int x, int y) { //加边    e[++el].y = y;    e[el].next = next[x];    next[x] = el;}LL dfs(int v) {    if(d[v]!=-INF) return d[v];    if(cnto[v]==0) return d[v] = val[v];    for(int i=next[v]; i; i=e[i].next) {        d[v] = max(d[v], (LL)val[v]+dfs(e[i].y));    }    return d[v];}int main() {    while(scanf("%d%d", &V,&E) != EOF) {        el = 0;        rep1(i,V) next[i] = cnti[i] = cnto[i] = 0;        rep1(i,V) scanf("%d", val+i);        for(int i=0, x,y; i<E; i++) {            scanf("%d%d", &x,&y);            add(x,y);            cnti[y]++;            cnto[x]++;        }        LL ans = -INF;        rep1(i,V) d[i] = -INF;        rep1(i,V) {            if(cnti[i]==0) {                ans = max(ans, dfs(i));            }        }        printf("%I64d\n", ans);    }    return 0;}

法3:拓扑排序+dp,复杂度O(V+E)

#include <iostream>#include <cstdio>#include <cstring>#include <vector>#include <queue>using namespace std;#define rep1(i,n) for(int i=1;i<=n;i++)#define LL long long#define N 100001 //顶点数#define M 1000001 //边数//poj3249 spfa + dpconst LL INF = 1000000000000000; //1e15struct edge {    int y, next;};int V, E; //顶点数,边数LL d[N]; //最短距离int q[M], head, tail; //队列,头,尾(切记:手写数组必须开到 > M)edge e[M]; //E条边int next[N], el; //i的下一条边的位置,记录边数int val[N]; //节点权值int ind[N], outd[N]; //入度,出度void init() {    el = 0;    rep1(i,V) next[i] = ind[i] = outd[i] = 0;    rep1(i,V) d[i] = -INF;}inline void add(int x, int y) { //加边    e[++el].y = y;    e[el].next = next[x];    next[x] = el;}void tpo() { //拓扑排序    head = tail = 0;    rep1(i,V) if(ind[i]==0) {        d[i] = val[i];        q[tail++] = i;    }    while(head < tail) {        int x = q[head++];        for(int i=next[x]; i; i=e[i].next) {            int y = e[i].y;            d[y] = max(d[y], d[x]+val[y]);            ind[y]--;            if(ind[y]==0) q[tail++] = y;        }    }}int main() {    while(scanf("%d%d", &V,&E) != EOF) {        init();        rep1(i,V) scanf("%d", val+i);        for(int i=0, x,y; i<E; i++) {            scanf("%d%d", &x,&y);            add(x,y);            ind[y]++;            outd[x]++;        }        tpo();        LL ans = -INF;        rep1(i,V) if(outd[i]==0) ans = max(ans, d[i]);        printf("%I64d\n", ans);    }    return 0;}


原创粉丝点击