[UOJ 111][APIO 2015]Jakarta Skyscrapers(Dijkstra+pbds堆暴力)

13 篇文章 0 订阅
7 篇文章 0 订阅

题目链接

http://uoj.ac/problem/111

思路

显然每条狗在整个过程中只会连续地跳跃一段时间,这显然不用我多说了。
这样显然就是一个最短路问题(大雾),求0号狗所在柱子到1号狗所在柱子的最短路

于是有下面的几个做法。

1、36分做法

直接暴力加边,然后跑SPFA啥的最短路就OK了
但是题目似乎故意卡了下这种做法,因此这种做法虽然时间复杂度和57分做法一样,但是会炸掉内存

2、57分做法

其实就是在之前的36分做法基础上,没有建图,而是在做最短路的过程中,由暴力访问当前这条狗下一次可能落到的点来代替原来的遍历图中的后继点。由于一条柱子上可能有多个狗,因此要用vector存下每个柱子上的每条狗的跳跃距离
仅仅这样还不够,我们还需要用堆优化Dijkstra代替SPFA,因为此题的特殊性,边数显然远远大于点数,而且堆优化Dijkstra只要在堆顶访问到了终点就可以结束程序,明显优于SPFA
不仅如此,假如我们在一条新柱子上发现有狗,且这条新狗和原来的狗跳跃距离是一样的,那么我们不让原来的狗继续暴力跳下去找接力者了,因为这条新狗的跳跃距离和原来的狗一模一样,二者可以视为等价的

3、100分做法

其实这种做法还是乱搞2333
只不过是在57分做法的基础上,考虑到堆里可能有很多结点,必然有一些结点代表的标号是一样的,这样的话堆里有很多结点是无用的,浪费掉了,这些兀余的结点会大大增加整个堆的大小,并影响每次堆的操作的速度,因此我们用pb_ds库里的堆,这个堆支持modify操作,即直接修改堆中的结点,这样我们每次不需要将新状态入堆,而只需要维护 n 个迭代器,分别代表原来的n个柱子在堆中的位置(是用指针表示的),然后直接用新状态修改掉原来的旧状态即可。
仅仅这样还不够,此题的数据非常坑,每条柱子上可能有很多条狗的跳跃距离是一样的,我们必须特判,让这些狗当成一条狗来计算才行
这个奇葩的100分做法能够以5~10ms过掉大部分的点,小部分的点需要90ms,还是非常快的
不过我为了防止卡时,用手写的结构体代替pair,并且用了快速读入和inline大法,略微让程序快了点

代码

1、36分做法

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXV 51000
#define MAXE 20000000
#define INF 0x3f3f3f3f

using namespace std;

struct edge
{
    int u,v,w,next;
}edges[MAXE*2];

int head[MAXV],nCount=0;

void AddEdge(int U,int V,int W)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].w=W;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

int n,m,K;
int dist[MAXV];
bool inQueue[MAXV];
int q[2*MAXE],S,T;

void SPFA()
{
    memset(dist,INF,sizeof(dist));
    int h=0,t=1;
    q[h]=S;
    dist[S]=0,inQueue[S]=true;
    while(h<t)
    {
        int u=q[h++];
        inQueue[u]=false;
        for(int p=head[u];p!=-1;p=edges[p].next)
        {
            int v=edges[p].v;
            if(dist[u]+edges[p].w<dist[v])
            {
                dist[v]=dist[u]+edges[p].w;
                if(!inQueue[v])
                {
                    inQueue[v]=true;
                    q[t++]=v;
                }
            }
        }
    }
}

int b[MAXV],p[MAXV];
bool mark[MAXV];

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&b[i],&p[i]),mark[b[i]]=true;
    for(int i=1;i<=m;i++)
    {
        for(int j=b[i]+p[i],k=1;j<n;j+=p[i],k++)
            if(mark[j])
                AddEdge(b[i],j,k);
        for(int j=b[i]-p[i],k=1;j>=0;j-=p[i],k++)
            if(mark[j])
                AddEdge(b[i],j,k);
    }
    S=b[1],T=b[2];
    SPFA();
    if(dist[T]==INF) dist[T]=-1;
    printf("%d\n",dist[T]);
    return 0;
}

2、57分做法

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <vector>

#define MAXV 51000
#define MAXE 20000000
#define INF 0x3f3f3f3f

using namespace std;

struct edge
{
    int u,v,w,next;
}edges[MAXE*2];

int head[MAXV],nCount=0;

void AddEdge(int U,int V,int W)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].w=W;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

int n,m,K;
int dist[MAXV];
bool vis[MAXV];
int S,T;
int b[MAXV];
vector<int>p[MAXV];
bool mark[MAXV];

priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > pq;

int Heap_Dij()
{
    memset(dist,INF,sizeof(dist));
    pq.push(make_pair(0,S));
    dist[S]=0;
    while(!pq.empty())
    {
        pair<int,int>now=pq.top();
        pq.pop();
        int i=now.second;
        if(i==T) return now.first;
        if(vis[i]) continue;
        vis[i]=true;
        for(int t=0;t<p[i].size();t++)
        {
            int P=p[i][t];
            for(int j=i+P,k=1;j<n;j+=P,k++)
                if(mark[j]&&dist[i]+k<dist[j])
                {
                    pq.push(make_pair(now.first+k,j));
                    dist[j]=dist[i]+k;
                }
            for(int j=i-P,k=1;j>=0;j-=P,k++)
                if(mark[j]&&dist[i]+k<dist[j])
                {
                    pq.push(make_pair(now.first+k,j));
                    dist[j]=dist[i]+k;
                }
        }
    }
    return -1;
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%d",&b[i]);
        scanf("%d",&x);
        mark[b[i]]=true;
        p[b[i]].push_back(x);
    }
    S=b[1],T=b[2];
    printf("%d\n",Heap_Dij());
    return 0;
}

3、100分做法

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <vector>
#include <ext/pb_ds/priority_queue.hpp>

#define MAXV 30010
#define MAXE 20000000
#define INF 999999999+208

using namespace std;

struct Pair
{
    int first,second;
    Pair(){}
    Pair(int _f,int _s):first(_f),second(_s){}
};

inline bool operator>(Pair a,Pair b)
{
    return a.first>b.first;
}

int read()
{
    int ans=0;
    char ch=getchar();
    while(ch==' '||ch=='\n') ch=getchar();
    while(ch>='0'&&ch<='9')
    {
        ans=ans*10+ch-'0';
        ch=getchar();
    }
    return ans;
}

int n,m,K;
int dist[MAXV],poslist[MAXV],tot_diff=0;
bool vis[MAXV];
int S,T;
int b[MAXV];
vector<int>p[MAXV];
vector<int>::iterator it;
bool mark[MAXV];

typedef __gnu_pbds :: priority_queue<Pair,greater<Pair>,__gnu_pbds::thin_heap_tag > Heap;
//typedef __gnu_pbds::priority_queue <Pair,greater<Pair >,__gnu_pbds::thin_heap_tag> Heap;
Heap pq;
Heap::point_iterator posmark[MAXV];

inline void update(Pair pr)
{
    if(dist[pr.second]>pr.first)
    {
        pq.modify(posmark[pr.second],pr);
        dist[pr.second]=pr.first; //!!!!!!
    }
}

int Heap_Dij()
{
    memset(dist,127,sizeof(dist));
    dist[S]=0;
    for(int i=0;i<n;i++)
        posmark[i]=pq.push(Pair(dist[i],i));
    while(!pq.empty())
    {
        Pair now=pq.top();
        pq.pop();
        if(now.first>INF) return -1;
        int i=now.second;
        if(i==T) return now.first;
        if(vis[i]) continue;
        vis[i]=true;
        for(int t=0;t<p[i].size();t++)
        {
            int P=p[i][t];
            if(t>0&&P==p[i][t-1]) continue; //!!!!!
            for(int j=i+P,k=1;j<n;j+=P,k++)
            {
                update(Pair(now.first+k,j));
                it=lower_bound(p[j].begin(),p[j].end(),P);
                if(it!=p[j].end()&&*it==P) break;
            }
            for(int j=i-P,k=1;j>=0;j-=P,k++)
            {
                update(Pair(now.first+k,j));
                it=lower_bound(p[j].begin(),p[j].end(),P);
                if(it!=p[j].end()&&*it==P) break;
            }
        }
    }
    return -1;
}

int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int x;
        b[i]=read();
        x=read();
        p[b[i]].push_back(x);
    }
    for(int i=0;i<n;i++)
        sort(p[i].begin(),p[i].end());
    S=b[1],T=b[2];
    printf("%d\n",Heap_Dij());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值