总理同学的编程尝试

为大家见证传说中的初学者暴力编程(C/C++)

2018.1.24 T3 最短路径

调试了一下午终于调出来了,原来WA了九个点,竟然是因为“优雅”的建图打错了一个字母。。

最短路径

【题面】


题目描述

题目描述


输入格式

输入格式


输出格式

输出答案,为一个整数。


样例输入

样例输入


样例输出

2


数据范围

数据范围


【思路】

GQH大神太强了,现场想出标算。假如我们把那个有k个点的点集称为集合S,如果我们把这个集合分成两个互不相交的子集(并且使它们的并集为原集合)S1,S2,然后分别对S1S2进行缩点。

(缩点:用一个新点去代替原来集合中的所有的点,假如原来集合S1中有一个点u,那么u的所有出边都要从这个“新点”连出,u的所有入边都要连入这个“新点”。当然了,要重建一个图,并且这张新图中将不存在一个叫u的点。)

很显然(数学老师好像很不喜欢学生写显然),如果最终的“答案路径”的起点和终点被划分到了不同的集合,那么缩点之后的两个集合间的最短路就是最终的答案。(因为是有向图,我们可以分别从两个“新点”跑一次SPFA,求出S1S2,S2S1两个最短路。)

正确性:因为缩点之后原来所有的边仍然存在,因为不存在负边,最短路要么是两个新点之间的边,要么是某个新点内部的边,要么是经由新点之外的一条路径从一个新点到另一个新点。而缩点就相当于忽略了两个新点内部的边,但并没有改变其他的路径。【解释了上文:如果最终的“答案路径”的起点和终点被划分到了不同的集合,那么缩点之后的两个集合间的最短路就是最终的答案。】

那么我们怎样缩点呢?因为这条路径的起点和终点一定是两个不同的点(保证不存在自环,出题的大神好像在考试的时候说了)。两个点的编号不一样,说明它们至少有一位二进制位是不同的,那么我们可以枚举每一个二进制位,每次把集合S中,编号在这个二进制位上为0的点,记为S1;编号在这个二进制位上为1的点,记为S2,然后进行缩点、跑SPFA。我们只需要O(lgN)次划分就能保证得到正确答案。


【代码】

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <set>
#include <algorithm>
using namespace std;

#define maxn (100000 + 10)
#define inf  (0x7f7f7f7f)

int s[maxn], k, n, m;

struct edge{
    int f, t, c;
} edges[maxn];

struct Graph{
    int from[maxn], to[maxn], cost[maxn], nxt[maxn], ecnt;
    int fst[maxn], dis[maxn], vis[maxn];
    void addedge(int f, int t, int c){
        int ne = ++ecnt;
        from[ne] = f; to[ne] = t; cost[ne] = c;
        nxt[ne] = fst[f]; fst[f] = ne;
    }
    int SPFA(int S, int T){
        //printf("\nSPFA s= %d t=%d\n",s,t);
        memset(dis, 0x7f, sizeof(dis));
        memset(vis, 0x00, sizeof(vis));
        queue<int> q;
        q.push(S); vis[S] = 1; dis[S] = 0;
        while(!q.empty()){
            int x = q.front(); q.pop(); vis[x] = 0;
            //printf("x = %d\n", x);
            for(int i=fst[x]; i!=0; i=nxt[i]){
                int t = to[i];
                //printf("try t = %d\n",t);
                if(dis[t] > dis[x] + cost[i]){
                    dis[t] = dis[x] + cost[i];
                    //printf("t suc dis[t] = %d\n",dis[t]);
                    if(!vis[t]){ //?
                        q.push(t);
                        vis[t] = 1;
                    }
                }
            }
        }
        return dis[T];
    }
} G[17]; // 0 ~ 16

#define norm(x) ((x) ? 1 : 0)

int main(){
    freopen("shortest.in", "r", stdin);
    freopen("shortest.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++){
        scanf("%d%d%d", &edges[i].f, &edges[i].t, &edges[i].c);
    }
    scanf("%d", &k);
    for(int i=1; i<=k; i++){
        int x; scanf("%d", &x); s[x] = 1;
    }
    int ans = inf;
    for(int d=0; d<=16; d++){
        int bit = 1 << d;
        for(int i=1; i<=m; i++){ //add all edge
            int f = (!s[ edges[i].f ]) ? edges[i].f : (n + 1 + norm(edges[i].f & bit)) ;
            int t = (!s[ edges[i].t ]) ? edges[i].t : (n + 1 + norm(edges[i].t & bit)) ;
            G[d].addedge(f, t, edges[i].c);
        }
        int t = min(G[d].SPFA(n+1, n+2), G[d].SPFA(n+2, n+1));
        ans = min(ans, t);
    }
    printf("%d\n", ans);
    return 0;
}
/*
5 6
1 2 1
2 3 3
3 1 3
2 5 1
2 4 2
4 3 1
3
1 3 5
*/

【2018.1.25】

阅读更多
版权声明:文章纯属版主手敲,请同学们尊重版主的知识产权。 https://blog.csdn.net/GGN_2015/article/details/79155734
想对作者说点什么? 我来说一句

CTF-All-In-One 2018.1.24

2018年01月24日 3.62MB 下载

ApkToolBox_1.6.4

2018年03月20日 37.41MB 下载

用友T3操作流程完全手册

2015年04月01日 601KB 下载

用友通、T3数据字典

2014年11月04日 14.59MB 下载

没有更多推荐了,返回首页

不良信息举报

2018.1.24 T3 最短路径

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭