日常训练 20170531 探险

题意简述:每个溶洞和其他某些溶洞有喑道相连,两个溶洞之间可能有多条道路,也有可能没有,但没有一条暗道直接从自己连到自己。参赛者需要统一从一个大溶洞出发,并再次回到这个大溶洞。不能经过同一条暗道两次(起点编号为 1
对于 100% 的数据, n10000,m200000,1w,v10000
如果一条回路上有重复走暗道,那么和起点相连的重复暗道必然存在,因为如果离起点最近的重复暗道上的点是X,那么去掉X到X的回路,那么这条回路会变短。所以只要控制走出 1 和回到1的路径不重复即可。然后我就写了一个暴力,枚举了出去是哪条边,然后把反向边删掉,复杂度是 O( 起点度数 (nlogm+m))
比赛结束发现有3种以上姿势可以A掉这道题。
法一:暴力加个优化,当遇到 dis>nowans 就break掉。
法二:随机。一开始我以为是随机出边,但这样每次只有 1m 的概率成功,但他们高妙的随机是随机每个和起点相邻的边是入还是出,那么每次有 14 的概率成功。
法三:SPFA求起点到每个点的最短路和次短路,并且最短路和次短路从起点出发的边不一样,那么只要枚举一下回起点的边即可。
法三有绝对的正确性保证,但法一法二也很优秀啊。
法一:

#include<bits/stdc++.h>
const int N = 1e4 + 10;
const int M = 4e5 + 50;
const int INF = 1e9;
template <typename T> void read(T &x){
    x = 0; char c = getchar();
    for (; !isdigit(c); c = getchar());
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
}
int n,m,x,y,u,v,s,cnt,ans,first[N],dis[N];
struct edge{
    int y,v,next;
    bool flag;
}mp[M];
void ins(int x, int y, int v, int _v){
    mp[++s] = (edge) {y,v,first[x],1}; first[x] = s;
    mp[++s] = (edge) {x,_v,first[y],1}; first[y] = s;
}
struct rec{
    int x,dis;
}h[M];
void push(rec x){
    h[++cnt] = x;
    int i = cnt;
    while (i > 1 && h[i].dis < h[i >> 1].dis)
        std::swap(h[i],h[i >> 1]),
        i >>= 1;
}
rec poop(){
    rec ret = h[1];
    h[1] = h[cnt--];
    int i = 1;
    while (i*2<=cnt && h[i*2].dis<h[1].dis || i*2+1<=cnt && h[i*2+1].dis<h[1].dis){
        int e = i * 2 + (i*2+1<=cnt && h[i*2+1].dis<h[i*2].dis);
        std::swap(h[i],h[e]);
        i = e;
    }
    return ret;
}
void Dijkstra(int x, int walked){
    for (int i=1; i<=n; i++) dis[i] = INF;
    dis[x] = walked;
    push((rec){x,dis[x]});
    while (cnt){
        rec p = poop();
        while (cnt && dis[p.x] != p.dis) p = poop();
        if (dis[p.x] != p.dis) continue;
        for (int t=first[p.x]; t; t=mp[t].next)
            if (mp[t].flag && dis[p.x] + mp[t].v < dis[mp[t].y] && dis[p.x] + mp[t].v < ans)
                dis[mp[t].y] = dis[p.x] + mp[t].v,
                push((rec){mp[t].y,dis[mp[t].y]});
    }
    ans = std::min(ans,dis[1]);
}
int main(){
    read(n);read(m);
    s = 1;
    while (m--)
        read(x),read(y),read(u),read(v),
        ins(x,y,u,v);
    ans = INF;
    for (int t=first[1]; t; t=mp[t].next)
        mp[t^1].flag = 0,
        Dijkstra(mp[t].y,mp[t].v),
        mp[t^1].flag = 1;
    printf("%d\n",ans);
    return 0;
}

法二:

#include<bits/stdc++.h>
const int N = 1e4 + 10;
const int M = 4e5 + 50;
const int INF = 1e9;
template <typename T> void read(T &x){
    x = 0; char c = getchar();
    for (; !isdigit(c); c = getchar());
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
}
int n,m,x,y,u,v,s,cnt,ans,first[N],dis[N],ty[M];
struct edge{
    int y,v,next;
    bool flag;
}mp[M];
void ins(int x, int y, int v, int _v){
    mp[++s] = (edge) {y,v,first[x],1}; first[x] = s;
    mp[++s] = (edge) {x,_v,first[y],1}; first[y] = s;
}
struct rec{
    int x,dis;
}h[M];
void push(rec x){
    h[++cnt] = x;
    int i = cnt;
    while (i > 1 && h[i].dis < h[i >> 1].dis)
        std::swap(h[i],h[i >> 1]),
        i >>= 1;
}
rec poop(){
    rec ret = h[1];
    h[1] = h[cnt--];
    int i = 1;
    while (i*2<=cnt && h[i*2].dis<h[1].dis || i*2+1<=cnt && h[i*2+1].dis<h[1].dis){
        int e = i * 2 + (i*2+1<=cnt && h[i*2+1].dis<h[i*2].dis);
        std::swap(h[i],h[e]);
        i = e;
    }
    return ret;
}
void build() {
    for (int t=first[1]; t; t=mp[t].next)
        if (rand() % 2)
            mp[t].flag = 0,
            mp[t^1].flag = 1;
        else
            mp[t].flag = 1,
            mp[t^1].flag = 0;
}
void Dijkstra(){
    for (int i=1; i<=n; i++) dis[i] = INF;
    for (int t=first[1]; t; t=mp[t].next)
        if (mp[t].flag)
            dis[mp[t].y] = std::min(dis[mp[t].y], mp[t].v),
            push((rec){mp[t].y, dis[mp[t].y]});
    while (cnt){
        rec p = poop();
        while (cnt && dis[p.x] != p.dis) p = poop();
        if (dis[p.x] != p.dis) continue;
        for (int t=first[p.x]; t; t=mp[t].next)
            if (mp[t].flag && dis[p.x] + mp[t].v < dis[mp[t].y])
                dis[mp[t].y] = dis[p.x] + mp[t].v,
                push((rec){mp[t].y,dis[mp[t].y]});
    }
    ans = std::min(ans,dis[1]);
}
int main(){
    srand(time(0));
    read(n);read(m);
    s = 1;
    for (int i=1; i <= m; i++)
        read(x),read(y),read(u),read(v),
        ins(x,y,u,v);
    ans = INF;
    for (int t=first[1]; t; t=mp[t].next)
        ty[++ty[0]] = t;
    for (int i=1; i <= 4 + 16 * (n <= 2000); i++)
        build(),
        Dijkstra();
    printf("%d\n",ans);
    return 0;
}

法三:

#include<bits/stdc++.h>
const int INF = 1e9;
const int N = 1e4 + 10;
const int M = 2e5 + 10;
template <typename T> void read(T &x) {
    x = 0; char c = getchar();
    for (; !isdigit(c); c = getchar());
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
}
struct edge {
    int y, v, next;
}mp[M*2];
int n, m, s, first[N], dis[N][2], from[N][2], q[N + 10];
bool inq[N];
void ins(int x, int y, int v, int _v) {
    mp[++s] = (edge) {y, v, first[x]}; first[x] = s;
    mp[++s] = (edge) {x, _v, first[y]}; first[y] = s;
}
void SPFA() {
    for (int i=1; i <= n; i++)
        dis[i][0] = dis[i][1] = INF;
    int head = 1, tail = 1;
    for (int t=first[1]; t; t=mp[t].next)
        if (mp[t].v < dis[mp[t].y][0]) {
            dis[mp[t].y][0] = mp[t].v;
            from[mp[t].y][0] = t;
            if (!inq[mp[t].y])
                inq[q[tail++] = mp[t].y] = 1;
        }
    while (head != tail) {
        int x = q[head];
        for (int t=first[x]; t; t=mp[t].next) {
            int y = mp[t].y;
            if (dis[x][0] + mp[t].v < dis[y][0]) {
                dis[y][1] = dis[y][0];
                from[y][1] = from[y][0];
                dis[y][0] = dis[x][0] + mp[t].v;
                from[y][0] = from[x][0];
                if (!inq[mp[t].y]) {
                    inq[q[tail++] = mp[t].y] = 1;
                    if (tail > N) tail = 1;
                }
                continue;
            }
            if (dis[x][0] + mp[t].v < dis[y][1] && from[y][0] != from[x][0]) {
                dis[y][1] = dis[x][0] + mp[t].v;
                from[y][1] = from[x][0];
                if (!inq[mp[t].y]) {
                    inq[q[tail++] = mp[t].y] = 1;
                    if (tail > N) tail = 1;
                }
            }
        }
        inq[q[head++]] = 0;
        if (head > N) head = 1;
    }
}
int main() {
    read(n);read(m);
    s = 1;
    int x, y, v, _v;
    for (int i=1; i <= m; i++)
        read(x), read(y), read(v), read(_v),
        ins(x, y, v, _v);
    SPFA();
    int ans = INF;
    for (int t=first[1]; t; t=mp[t].next)
        ans = std::min(ans, dis[mp[t].y][from[mp[t].y][0] == t] + mp[t^1].v);
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值