训练日记 2019.9.9 重阳节快乐

2019.9.9
今天也没啥太好说的,不过就是发现了几点最短路问题中常见到的问题,写一下吧。

最短路,看了半圈csdn感觉很迷,因为好像大部分人都没搞清dijkstra和spfa到底有什么区别,所有问题,不管图是有向图还是无向负权图,都清一色无脑上dijkstra,有时候对于新手真的很是误导。ACM,坦率来说除了少几部分内容,实际工作根本用不到,但是之所以受人欢迎是因为大家从中学到的高效程序设计的思维和对复杂度的理解可以对未来有很大的提升。这不能丢掉啊,我很看不懂一些人拿ElogV甚至没有经过堆优化的dijkstra的平方算法去做一道线性SPFA算法可以解决的问题是什么情况,没错都能过黑盒子测试,但是那多出来的大几百ms的时间到了大项目可能就是天差地别。所以各位遇到问题先读题,然后还是对症下药的好,dijkstra在很多问题上远没有BF和SPFA来得方便和快,别被误导了。总结了些问题,最短路排查故障和解题的一些小指南。

用spfa算法的Time Limit Exceeded问题

这个问题还是挺常见的,正常情况下在执行一次SPFA求最短路问题的时候,如果跑3000以上的ms基本可以排除是方法用错,这个时候一般检查下是不是没有把判断负权环的更新次数update数组这部分加上。因为有些题不需要判断负权环,有时候手懒就没有加上判断负权边的数组,这样的话spfa一直跳不出来,肯定会超时的。注意下。

输入低于1000的图

首先看到1000左右的输入就注意,ACM的赛场基本上出题规律都是时间和算法复杂度配合起来刚好能过线,一般不存在说1000单给一道用spfa或者bf的题,每个算法都是有相应的输入匹配段的,以下是个人总结的一些算法适用的区间,可以参考下,首先是,ACM的裁判能够接受的最大运算量是1亿左右
Dijkstra:输入大概是10000 – 100000

bellmanford: 10000 – 50000

SPFA:10000 – 100000

Floyd: 500 – 1000

注意下BF和SPFA中间如果条件允许优先选择SPFA。

图的反转

通俗来讲,就是如果在一道题你突然发现需要做n遍某种从任意点到目标点距离的最短路算法,首先考虑将图反转从源点做一遍最短路算法。最近遇到了好多问题都是这样。那么大的常数,如果一个循环里套一个算法执行n遍那可真是正中出题人下怀了。如果发现自己在写此类代码,立刻转换思路hhhh。

有向图和无向图

这个问题估计csdn到现在也没有一个明确的说法该不该用dijkstra, dijkstra在算法书上写的是只能用于无向图的计算,不能计算任何有向图。但是实际过程中表现出人意料地好,以至于一部分选手看到题目便往上面敲模板。个人的经验来看不推崇盲目使用。大家时刻牢记,dijkstra的算法复杂度是ElogV,有些国内自己动手用数组实现的是平方次的复杂度,这跟别的算法比仅仅可以称作中规中矩,如果出题人把这道题常数开大,那么dijkstra必定会超时。

不仅如此,dijkstra无法判断负权环,如果拿这个算法过了有向图我只能说那是出题人没有刻意利用有向图的性质去刁难选手而已。就像BF改双向松弛在一些无向图也能一展身手一样,虽然是够用,但显然不是最高明的做法,而且一旦WA掉那很有可能整场比赛你都别想通过除了换算法意外的方式提交通过了。而且,举个例子,克鲁斯卡尔算法简单易懂,但是用在稠密图上就会达到立方次复杂度,如果简单易懂,那还要普里姆算法干什么?选手学的应该是高效的思维,而不是过考试,NOI选手现场发明算法的事情也不绝于耳,而且都能AC,但是大部分到目前为止都实在经不起更深层次的推敲。

这道题做了两个小时了还没AC

个人经验,如果不是老手,遇到最短路多加一个变量的这种问题赶紧跳。不是说让大家放弃坚持下去的能力,但大部分这种题如果你1一个小时没有做出来,那两个小时甚至于一天你也做不出来。都说老田你这不是废话么?为什么这样说,国内的题的变量从来不会无缘无故加上或者删掉,如果这么做了,那一定是得上动态链表和动态规划,这里面你看吧,调试很麻烦的,尤其是在COI赛制,这种题很可怕,可能就会浪费时间以至于排名雪崩。不如换个思路,先去换换脑子,然后再商量这道题。

好了,今天就说这么多了,放一下今天AC的题目吧,dijkstra的题目
题目: HDU 2680 Choose the best route
链接: HDU2680 Choose the best route

#include 
using namespace std;
#define limit 200000 + 5
#define INF 0x3f3f3f3f
#define lowbit(i) i&(-i)//定义lowbit函数
#define EPS 1e-6
#define ff(a) printf("%d\n",a );
typedef long long ll;
void read(int &x){
    char ch = getchar();x = 0;
    for (; ch < '0' || ch > '9'; ch = getchar());
    for (; ch >='0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
}//快读
struct edge{
    int to , weight;
    edge() = default;
    edge(int tt, int ww) : to(tt), weight(ww){}
    bool operator<(const edge & rhs)const{
        return weight > rhs.weight;
    }
};
vector >v;
int vis[limit],dist[limit],update[limit];
int n, m,k;
int status;
vectorresult;//储存最小的
int spfa(int s){
    for(int i = 0 ; i <= n ; ++i)update[i] =  vis[i] = 0, dist[i] = INF;
    queueq;
    vis[s] = 1;
    dist[s] = 0;
    q.push(s);
    while(q.size()){
        int vs = q.front();
        q.pop();
        vis[vs] = 0;
        for(int i = 0 ; i < v[vs].size() ; ++i){
            int ve = v[vs][i].to, weight = v[vs][i].weight;
            if(dist[vs] != INF && dist[vs] + weight < dist[ve]){
                dist[ve] = dist[vs] + weight;
                update[ve]++;
                if(update[ve] >= n)return 0;
                if(!vis[ve]){
                    vis[ve] = 1;
                    q.push(ve);
                }
            }
        }
    }
    return 1;
}
vectorss;
int main(){
    while(scanf("%d%d%d" , &n , & m, &k) != EOF) {
        v.resize(n + 1);
        for(int i = 0 ; i < m ; ++i){
            int a,b,w;
            scanf("%d%d%d" , &a , &b ,&w);
            v[b].push_back(edge(a,w));
        }
        int t;
        scanf("%d" , &t);
        while(t--){
            int a;
            scanf("%d" , &a);
            ss.push_back(a);
        }
        int minV = INF;
        spfa(k);
        for(int i = 0 ; i < ss.size() ; ++i){
                minV = min(dist[ss[i]] , minV);//没有就更新
        }
        if(minV != INF){//yes
            ff(minV);
        }else{
            ff(-1);
        }
        ss.clear();
        v.clear();
    }
    return 0;
}

你可能感兴趣的:(ICPC)