2021.8.6 图论

  好嘞,接着学

  欧拉回路,欧拉通路 get

  链式前向星 get

  与欧拉回路的对比:欧拉回路是指不重复地走过所有路径的回路;哈密尔顿回路是指不重复地走过所有点并且最后回到起点的回路。

  笑死,csdn的编译器中 \geqslant 是>=

  拓扑排序,AOV网 get

  AOE网 get (并没看出来与aov网有什么不同

  AOE AOV网不同可能在于存储方式

  将无穷大设为 0x3f3f3f3f

  floyd算法 get

  floyd算法主体:

for(int k=1;k<=n;k++)//第一重循环为i→j的中间点k
    for(int i=1;i<=n;i++)//第二重循环为起点i
        for(int j=1;j<=n;j++)//第三重循环为终点j
            if(dis[i][j]>dis[i][k]+dis[k][j])//如果i→k的距离加上k→j的距离小于i→j的距离
                dis[i][j]=dis[i][k]+dis[k][j];//更新最短路径

  搞几个板子(扭

  floyd矩阵存入

  

void floyd(int n) {
    for (int k = 1; k <= n; k++) {         //枚举中间点
        for (int i = 1; i <= n; i++) {     //枚举起点
            for (int j = 1; j <= n; j++) { //枚举终点
                if (G[i][k] < INF && G[k][j] < INF) {
                    if (G[i][j] > G[i][k] + G[k][j]) { //松弛操作
                        G[i][j] = G[i][k] + G[k][j];
                        path[i][j] = path[i][k]; //更新路径
                    } 
                    else if (
                        G[i][j] == G[i][k] + G[k][j] && path[i][j] > path[i][k]) { //在最短路相同的情况下,更新字典序最小的路径
                        path[i][j] = path[i][k]; //最终path中存的是字典序最小的路径
                    }
                }
            }
        }
    }
}

  SPFA(标准版

void SPFA(int S) {
    memset(vis, false, sizeof(vis));
    memset(dis, INF, sizeof(dis));
    dis[S] = 0;
 
    queue<int> Q;
    Q.push(S);
 
    while (!Q.empty()) {
        int x = Q.front();
        Q.pop();
        vis[x] = false;
        for (int i = head[x]; i != -1; i = edge[i].next) {
            int to = edge[i].to;
            if (dis[to] > dis[x] + edge[i].dis) {
                dis[to] = dis[x] + edge[i].dis;
                if (!vis[to]) {
                    vis[to] = true;
                    Q.push(to);
                }
            }
        }
    }
}

  带负环判断的SPFA(扭

struct SPFA {
    int n, m;
    Edge edges[N]; //所有的边信息
    int head[N];   //每个节点邻接表的头
    int next[N];   //每个点的下一条边
    int pre[N];    //最短路中的上一条弧
    bool vis[N];
    int dis[N];
    int cnt[N]; //进队次数
 
    void init(int n) {
        this->n = n;
        this->m = 0;
        memset(head, -1, sizeof(head));
    }
 
    void AddEdge(int from, int to, int dist) {
        edges[m] = Edge(from, to, dist);
        next[m] = head[from];
        head[from] = m++;
    }
 
    bool negativeCycle(int s) { //判负环
        memset(vis, false, sizeof(vis));
        memset(cnt, 0, sizeof(cnt));
        memset(dis, INF, sizeof(dis));
        dis[s] = 0;
 
        queue<int> Q;
        Q.push(s);
 
        while (!Q.empty()) {
            int x = Q.front();
            Q.pop();
            vis[x] = false;
            for (int i = head[x]; i != -1; i = next[i]) {
                Edge &e = edges[i];
                if (dis[e.to] > dis[x] + e.dis) {
                    dis[e.to] = dis[x] + e.dis;
                    pre[e.to] = i;
                    if (!vis[e.to]) {
                        vis[e.to] = true;
                        Q.push(e.to);
                        if (++cnt[e.to] > n)
                            return true;
                    }
                }
            }
        }
        return false;
    }
} spfa;

  大概是存储板子(ww

struct Edge {
    int to, next;
    int dis;
} edge[N];
int head[N], tot;
bool vis[N];
int dis[N];
void addEdge(int x, int y, int dis) {
    edge[++tot].to = y;
    edge[tot].dis = dis;
    edge[tot].next = head[x];
    head[x] = tot;
}

  9:47惹

  Ford算法不看了,貌似能被SPFA概括?

  Dijkstra 算法(矩阵存入

int G[N][N];//G[i][j]表示i到j的有向边长
bool vis[N];//表示w[i]是否已经计算完
void dijkstra(int n,int s){
    for(int i=1;i<=n;i++){
        int x;//x标记当前最短w的点
        int min_dis=INF;//记录当前最小距离
 
        for(int y=1;y<=n;y++){
            if(!vis[y] && min_dis>=dis[y]){
                x=y;
                min_dis=dis[x];
            }
        }
 
        vis[x]=true;
 
        for(int y=1;y<=n;y++) 
            dis[y]=min(dis[y],dis[x]+G[x][y]);
    }
}

  我服了,咋那么多最短路算法。

  传统美德

  哦,没事了,下一节不是最短路

  (呼

  差分约束系统,看起来好难阿

  看起来好神奇(思索

  xswl,这个差分甚么的,不辍,但好像用不上 get

  学到树了(好快啊

  就是板子都还没背

  累了,水一会儿(确信

  10:08

  最小生成树prim算法(适合稠密图 get

int n,m;//n个点m条边
int G[N][N];
int dis[N];
bool vis[N];
int MST;
void Prim(){
    for(int i=1;i<=n;i++){
        int k;
        int minn=INF;
        for(int j=1;j<=n;j++){//枚举所有点
            if(!vis[j]&&dis[j]<minn){//寻找与白点相连的权值最小的蓝点u
                minn=dis[j];
                k=j;
            }
        }
        vis[k]=true;//蓝点u加入生成树,标记为白点
        for(int j=1;j<=n;j++)//修改所有与u相连的蓝点
            if(!vis[j]&&dis[j]>G[k][j])
                dis[j]=G[k][j];
    }
 
    MST=0;
    for(int i=1;i<=n;i++)//权值和的计算
        MST+=dis[i];
}

  最小生成树kruskal(适合稀疏图 get

struct Edge {
    int x, y;
    int dis;
    bool operator<(Edge K) const { return dis < K.dis; }
} edge[N];
int father[N];
int Find(int x) {
    if (father[x] == x)
        return x;
    return father[x] = Find(father[x]);
}
int kruskal(int n, int m) {
    for (int i = 1; i <= n; i++) //并查集初始化
        father[i] = i;
    sort(edge + 1, edge + m + 1); //升序排序
 
    int MST = 0;
    int edgeNum = 0; //边数
    for (int i = 1; i <= m; i++) {
        int x = Find(edge[i].x);
        int y = Find(edge[i].y);
        if (x != y) {
            father[x] = y;
            MST += edge[i].dis;
            edgeNum++;
        }
        if (edgeNum == n - 1) { // n-1条边时停止
            return MST;
        }
    }
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; ++i)
        scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].dis);
    int MST = kruskal(n, m);
    printf("%d\n", MST);
    return 0;
}

  生成树计数(基尔霍夫矩阵 神奇! get

LL K[N][N];
LL gauss(int n){//求矩阵K的n-1阶顺序主子式
    LL res=1;
    for(int i=1;i<=n-1;i++){//枚举主对角线上第i个元素
        for(int j=i+1;j<=n-1;j++){//枚举剩下的行
            while(K[j][i]){//辗转相除
                int t=K[i][i]/K[j][i];
                for(int k=i;k<=n-1;k++)//转为倒三角
                    K[i][k]=(K[i][k]-t*K[j][k]+MOD)%MOD;
                swap(K[i],K[j]);//交换i、j两行
                res=-res;//取负
            }
        }
        res=(res*K[i][i])%MOD;
    }
    return (res+MOD)%MOD;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    memset(K,0,sizeof(K));
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        K[x][x]++;
        K[y][y]++;
        K[x][y]--;
        K[y][x]--;
    }
    printf("%lld\n",gauss(n));
    return 0;
}

  最小生成树计数(加强版

struct Edge {
    int x,y;
    int dis;
    bool operator < (const Edge &rhs) const {
        return dis<rhs.dis;
    }
} edge[N],tr[N];
int n,m;
int father[N];
int G[N][N];
int tot,bel[N],val[N];
int Find(int x) {
    if(father[x]!=x)
        return father[x]=Find(father[x]);
    return x;
}
int Gauss(int n) {
    int res=1;
    for(int i=1; i<=n; i++) {
        for(int k=i+1; k<=n; k++) {
            while(G[k][i]) {
                int d=G[i][i]/G[k][i];
                for(int j=i; j<=n; j++)
                    G[i][j]=(G[i][j]-1LL*d*G[k][j]%MOD+MOD)%MOD;
                swap(G[i],G[k]);
                res=-res;
            }
        }
        res=1LL*res*G[i][i]%MOD,res=(res+MOD)%MOD;
    }
    return res;
}
int Kruskal() {
    sort(edge+1,edge+m+1);
    for(int i=1; i<=n; i++)
        father[i]=i;
    int cnt=0;
    for(int i=1; i<=m; i++) {
        int fu=Find(edge[i].x);
        int fv=Find(edge[i].y);
        if(fu==fv)
            continue;
        father[fu]=fv,tr[++cnt]=edge[i];
        if(edge[i].dis!=val[tot])
            val[++tot]=edge[i].dis;
    }
    return cnt;
}
void addTreeEdge(int v) {
    for(int i=1; i<n&&tr[i].dis!=v; i++){
        int x=tr[i].x;
        int y=tr[i].y;
        father[Find(x)]=Find(y);
    }
    for(int i=n-1; i&&tr[i].dis!=v; i--){
        int x=tr[i].x;
        int y=tr[i].y;
        father[Find(x)]=Find(y);
    }
}
void rebuild(int v) {
    memset(G,0,sizeof(G));
    for(int i=1; i<=m; i++){
        if(edge[i].dis==v){
            int x=bel[edge[i].x];
            int y=bel[edge[i].y];
            G[x][y]--;
            G[y][x]--;
            G[x][x]++;
            G[y][y]++;
        }
    }
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++)
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].dis);
 
    int cnt=Kruskal();
    if(cnt!=n-1) {
        printf("0\n");
    }
    else{
        int res=1;
        for(int i=1; i<=tot; i++) {
            for(int i=1; i<=n; i++)
                father[i]=i;
 
            addTreeEdge(val[i]);
 
            int blo=0;
            for(int i=1; i<=n; i++)
                if(Find(i)==i)
                    bel[i]=++blo;
            for(int i=1; i<=n; i++)
                bel[i]=bel[Find(i)];
 
            rebuild(val[i]);
            res=1LL*res*Gauss(blo-1)%MOD;
        }
        printf("%d\n",res);
    }
    return 0;
}

  矩阵的行列式......不想学ww,看到定义就头大(眨眼

  yysy,看完了,看懂啦,也不知能记住几天

  头大

  曼哈顿距离最小生成树 没看懂 我服了 看不进去

  爬去调调今天集训的题了

 

上一篇:HDU - 2196 - Computer( 树形dp )


下一篇:最短路径算法整理