luogu解题报告:P1186玛丽卡【图论/最短路/堆优化dijkstra】

题目见https://www.luogu.org/problem/show?pid=1186

分析

一个简单的思路是枚举删除每一条边,然后跑堆优化dijkstra算法。但这样的复杂度恐怕过大。因此有一个简单的优化思路:第一次跑dijkstra时记录路径上的点。显然,堵车的路一定处于这些点之间,因为如果不是,玛丽卡走原先的最短路一定最短。因此只需要枚举删除处于最短路上的点之间的边,再跑堆优化dijkstra即可。代码使用zkw树优化。

示例代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

// basic of graph
struct p {
    int to, next, dis;
}edge[3000000];
int head[1005], top = 0;
void push(int i, int j, int k)
{
    edge[++top].to = j;
    edge[top].dis  = k;
    edge[top].next = head[i];
    head[i] = top;
}

// a heap

class zkw_tree {
    int dat[2050];
    int from[2050];
    int n;
    public:
        zkw_tree()
        {
            memset(dat, 127/3, sizeof dat);
            for (int i = 1; i < 2050; i++)
                from[i] = i-n+1;
        }
        void clear()
        {
            memset(dat, 127/3, sizeof dat);
            for (int i = 1; i < 2050; i++)
                from[i] = i-n+1;
        }
        void init(int num)
        {
            n = num;
        }
        void modify(int i, int new_val)
        {
            dat[i+n-1] = new_val;
            for (int pos = (i+n-1)>>1; pos; pos>>=1) {
                int lft = pos<<1, rgt = (pos<<1)+1;
                if (dat[lft] <= dat[rgt]) {
                    dat[pos] = dat[lft];
                    from[pos] = from[lft];
                } else {
                    dat[pos] = dat[rgt];
                    from[pos] = from[rgt];
                }
            }
        }
        int query()
        {
            return from[1];
        }
        int top()
        {
            return dat[1];
        }
}zkw_heap;

// some var & func
int forbid_s = 0, forbid_t = 0;
int n, m;
bool eq(int a, int b, int c, int d)
{
    return (a == c && b == d) || (a == d && b == c);
}

// dijkstra
int dis[1005], pre[1005];
int dijkstra(bool take_path = false)
{
    memset(dis, 127/3, sizeof dis);
    dis[1] = 0;
    zkw_heap.clear();
    zkw_heap.modify(1, 0);
    for (int i = 1; i < n; i++) {
        int k = zkw_heap.query();
        dis[k] = zkw_heap.top(); zkw_heap.modify(k, 233333333);
        //cout << k << " " << dis[k] << endl;
        if (k == n) break;
        for (int j = head[k]; j; j = edge[j].next) {
            int to = edge[j].to, d = edge[j].dis;
            if (dis[to] > dis[k]+d && !eq(k, to, forbid_s, forbid_t)) {
                dis[to] = dis[k]+d;
                if (take_path)
                    pre[to] = k;
                zkw_heap.modify(to, dis[to]);
            }
        }
    }
    return dis[n];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值