BZOJ 3669 NOI 2014 魔法森林 最短路/LCT

题目大意:给一个无向图,每个边有两个权值,a和b,要想经过这条边,身上所携带的a和b都需要大于等于边的权值,否则就会遭到攻击。求从1到n身上携带最少的a+b的值


思路:1、LCT(不会)

            2、弱弱的用最短路。

利用SPFA求从起点到终点的最短最长距离是肯定能想到的,但是跑m次SPFA是肯定会超时的。

有一个十分神奇的想法,按照a的权值排序,从小到大一次将每一条边加入到图中,然后跑SPFA。dis数组不用清极大值,每次SPFA时只向队列里加入新加入的边的两个端点。这是两个很有效的剪枝。


CODE(不加堆优化,BZOJ3880ms):


#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 600010
#define INF 0x7f7f7f7f
using namespace std;
 
queue<int> q;
 
struct Complex{
    int x,y;
    int a,b;
    bool operator <(const Complex& x)const {
        return a < x.a;
    }
}edge[MAX];
 
int points,edges;
int ans = INF;
int head[MAX],total;
int next[MAX],aim[MAX],length[MAX];
int f[MAX];
bool v[MAX];
 
inline void Add(int x,int y,int len);
void SPFA();
 
int main()
{
    cin >> points >> edges;
    for(int i = 1;i <= edges; ++i)
        scanf("%d%d%d%d",&edge[i].x,&edge[i].y,&edge[i].a,&edge[i].b);
    sort(edge + 1,edge + edges + 1);
    memset(f,0x3f,sizeof(f));
    f[1] = 0;
    for(int i = 1;i <= edges; ++i) {
        Add(edge[i].x,edge[i].y,edge[i].b);
        Add(edge[i].y,edge[i].x,edge[i].b);
        q.push(edge[i].x),v[edge[i].x] = true;
        q.push(edge[i].y),v[edge[i].y] = true;
        if(edge[i].a != edge[i + 1].a)
            SPFA();
        ans = min(ans,edge[i].a + f[points]);
    }
    if(ans >= 0x3f3f3f3f)    ans = -1;
    cout << ans;
    return 0;
}
 
inline void Add(int x,int y,int len)
{
    next[++total] = head[x];
    aim[total] = y;
    length[total] = len;
    head[x] = total; 
}
 
void SPFA()
{
    while(!q.empty()) {
        int x = q.front(); q.pop();
        v[x] = false;
        for(int i = head[x];i;i = next[i])
            if(f[aim[i]] > max(length[i],f[x])) {
                f[aim[i]] = max(length[i],f[x]);
                if(!v[aim[i]])
                    q.push(aim[i]);
            }
    }
}


CODE(堆优化+SPFA,BZOJ上开了-O2,pq的效率比较高,3056ms,不开-O2的话会比不加优化的慢,慎用!):


#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 600010
#define INF 0x7f7f7f7f
using namespace std;
 
struct Point;
 
priority_queue<Point> q;
 
struct Complex{
    int x,y;
    int a,b;
    bool operator <(const Complex& x)const {
        return a < x.a;
    }
}edge[MAX];
struct Point{
    int p;
    Point(int a):p(a) {}
    bool operator <(const Point& a)const;
};
 
int points,edges;
int ans = INF;
int head[MAX],total;
int next[MAX],aim[MAX],length[MAX];
int f[MAX];
bool v[MAX];
 
inline void Add(int x,int y,int len);
void SPFA();
 
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    cin >> points >> edges;
    for(int i = 1;i <= edges; ++i)
        scanf("%d%d%d%d",&edge[i].x,&edge[i].y,&edge[i].a,&edge[i].b);
    sort(edge + 1,edge + edges + 1);
    memset(f,0x3f,sizeof(f));
    f[1] = 0;
    for(int i = 1;i <= edges; ++i) {
        Add(edge[i].x,edge[i].y,edge[i].b);
        Add(edge[i].y,edge[i].x,edge[i].b);
        q.push(Point(edge[i].x)),v[edge[i].x] = true;
        q.push(Point(edge[i].y)),v[edge[i].y] = true;
        if(edge[i].a != edge[i + 1].a)
            SPFA();
        ans = min(ans,edge[i].a + f[points]);
    }
    if(ans >= 0x3f3f3f3f)    ans = -1;
    cout << ans;
    return 0;
}
 
inline void Add(int x,int y,int len)
{
    next[++total] = head[x];
    aim[total] = y;
    length[total] = len;
    head[x] = total; 
}
 
void SPFA()
{
    while(!q.empty()) {
        Point x = q.top(); q.pop();
        v[x.p] = false;
        for(int i = head[x.p];i;i = next[i])
            if(f[aim[i]] > max(length[i],f[x.p])) {
                f[aim[i]] = max(length[i],f[x.p]);
                if(!v[aim[i]])
                    q.push(Point(aim[i]));
            }
    }
}
 
bool Point :: operator <(const Point& a)const {
    return f[a.p] < f[p];
}



你可能感兴趣的:(SPFA,bzoj,NOI2014,堆优化SPFA)