最短路,前缀和优化连边,Dijkstra(UESTC 482,Charitable Exchange)

来源:互联网 发布:电梯破解软件 编辑:程序博客网 时间:2024/05/16 06:18

http://acm.uestc.edu.cn/#/problem/show/482


做了这道题目很有收获,一方面是熟练了优化连边的技巧,另一方面是对Dijkstra有了新的感悟。


关于优化连边的做法:

首先离散化,将1e9范围的数离散化,重新分配编号,这样最多有2e5个节点。

很显然,题目中的边是一种区间到单点的边,所以可以考虑线段树优化连边,可能是因为自己不够熟练,实现得不好,所以超内存了,但是看到有人这样做过了的。

但是有更好的优化方式,可以有更小常数的时空复杂度。

我们发现起点区间有一个性质,那就是右边界一定在最右边,一共有n个区间,所以就不用线段树优化了,可以后缀和优化,每个区间新建一个节点,直接区间到单点连边,然后每个区间和前面的区间连边,跑Dijkstra就好了。


关于对Dijkstra的新感悟:

主要是看了这篇博客的解法,在思考为什么正确的过程中对Dijkstra有了更深的认识。

http://blog.csdn.net/xinag578/article/details/50883997

博主说是广搜+优先队列优化,其实本质上就是Dijkstra的O(nlogn)优化啦。

n条边各用一次,因此最多产生n个新节点,放入优先队列中,时间复杂度O(nlogn)。

我一直疑惑为什么要挑一个t最小的点,然后把所有能用的边都用了,就可以得到正解。主要是疑惑为什么敢这么大胆地把用过的边都丢掉,难道保证这样做物尽其用了吗?

通过和Dijkstra的对比后发现,它们的做法其实没有任何区别,Dijkstra也是挑一个d最小的HeapNode,然后把所有从这个Node出发的边都用掉,更新其他节点。

由于每个节点只done一次,所以每条边只用一次,一共e条边,产生的新节点个数其实不太好估计,会有重复。仔细想了想最优是O(n)最差是O(n^2),但是那不重要,因为要取对数,平方就变成常数了,因此时间复杂度为O(elogn)。


下面讲一讲为什么敢这么大胆地把用过的边丢掉,即证明物尽其用了。

首先,边的作用就是为了更新节点,如果能保证这条边更新的情况是最完美的,那就必然物尽其用了。

何时才最完美呢?一条边的终端是固定的,边的长度也是固定的,那么只有当边的起始端的d是最小的时候,更新就是最完美的。

Dijkstra算法每次挑出d最小的未done的点,遍历所有出边,更细其他节点,此时的更新是最完美的,所以边用掉就可以丢了,不再用了。

上面那道题目也是如此,每次跳出t最小的点,然后把所有能用的边都用了,去更新其他节点,此时的更新也是最完美的,所以更新完后边也可以扔掉了。

因为m的范围太大,所以没法开那么大的数组来done,想done只能用stl或hash,但是不done不会错,只会导致时间复杂度升高,最多甚至可达立方级别,但是取对数后也变成常数了,所以也没关系。


优化连边代码:

#include<bits/stdc++.h>#define ls (now<<1)#define rs (ls|1)#define mid ((l+r)>>1)using namespace std;typedef long long ll;const ll maxn = 100010;const ll inf = LONG_LONG_MAX>>2;struct Edge{    ll from,to,dist;};struct HeapNode{    ll d,u;    bool operator < (const HeapNode& rhs) const    {        return d>rhs.d;    }};struct Dijkstra{    ll n,m;    vector<Edge>edges;    vector<ll>G[maxn<<2];    ll done[maxn<<2];    ll d[maxn<<2];    ll p[maxn<<2];    void init(ll n)    {        this->n=n;        edges.clear();        for(ll i=1;i<=n;i++) G[i].clear();    }    void AddEdge(ll from,ll to,ll dist)    {        edges.push_back((Edge){from,to,dist});        m=edges.size();        G[from].push_back(m-1);    }    void dijkstra(ll s)    {        for(ll i=1;i<=n;i++)        {            d[i]=inf;            done[i]=0;        }        d[s]=0;        priority_queue<HeapNode>q;        q.push((HeapNode){0,s});        while(!q.empty())        {            HeapNode now = q.top();            q.pop();            ll u = now.u;            if(done[u]) continue;            done[u]=1;            for(ll i=0;i<(ll)G[u].size();i++)            {                Edge& e = edges[G[u][i]];                if(d[e.to]>d[e.from]+e.dist)                {                    d[e.to]=d[e.from]+e.dist;                    p[e.to]=G[u][i];                    q.push((HeapNode){d[e.to],e.to});                }            }        }    }}DIJ;ll N,M;ll u[maxn];ll v[maxn];ll d[maxn];vector<ll>vec;map<ll,ll>id;ll cnt;void solve(){    id.clear();    cnt=0;    vec.clear();    scanf("%lld %lld",&N,&M);    for(ll i=0;i<N;i++)    {        scanf("%lld %lld %lld",v+i,u+i,d+i);        if(u[i]==v[i])        {            N--;            i--;        }        else        {            if(u[i]>M) u[i]=M;            if(v[i]>M) v[i]=M;            vec.push_back(u[i]);            vec.push_back(v[i]);        }    }    vec.push_back(1);    vec.push_back(M);    sort(vec.begin(),vec.end());    vec.resize(unique(vec.begin(),vec.end())-vec.begin());    for(ll i=0;i<(ll)vec.size();i++) id[vec[i]]=++cnt;    DIJ.init(cnt<<1);    for(ll i=2;i<=cnt;i++) DIJ.AddEdge(i+cnt,i+cnt-1,0);    for(ll i=1;i<=cnt;i++) DIJ.AddEdge(i,i+cnt,0);    for(ll i=0;i<N;i++)    {        ll U=id[u[i]];        ll V=id[v[i]];        DIJ.AddEdge(U+cnt,V,d[i]);    }    DIJ.dijkstra(id[1]);    ll ans = DIJ.d[id[M]];    if(ans==inf) puts("-1");    else printf("%lld\n",ans);}int main(){    ll T;    scanf("%lld",&T);    for(ll t=1;t<=T;t++)    {        printf("Case #%lld: ",t);        solve();    }    return 0;}

0 0
原创粉丝点击