VUA~125 Numbering Paths

题目传送门

一、题目描述

给定一些组数据,每组数据由一个 nn 对整数 jk 组成。由EOF停止读入。每组 jiki 意味着存在一条由点 ji到点 ki​的 单向边。其中,所有 jiki中的最大值 +1 即为 最终矩阵边长 a。可能存在 自环。数据保证任意一个点的入度和出度都不大于 30

每组输出,包括一个字符串matrix for city,一个整数 t 表示从 0 开始的组数,以及一个 a×a 的矩阵 M,其中第 i 行第 j 个元素 M[i][j] 表示从点 ij不同路径数量。如果有无数条,在该位置输出 1严格要求行末没有空格

二、题目解析

前置芝士:Floyd

Floyd 是一种求多源最短路的方法,其本质思想是动态规划。

dp[i][j] 表示 i 节点到 j 节点之间的 最短路,那么可以得到状态转移方程:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])(1kn)

注意 dp 时外层循环先枚举 k,因为 Floyd 本来是一个三维 dpdp[k][i][j] 表示 i 节点到 j 节点之间只能经过 1k 这几个节点的最短路),通过类似完全背包的空间优化才把空间复杂度降到二维。

具体做法
对于这道题,我们可以对状态转移方程进行修改。 设 dp[i][j] 表示 i 节点到 j 节点之间的路径个数。同样的道理,我们需要枚举中间跳板 k

显然,根据乘法原理i 节点到 j 节点之间的路径个数 等于 i 节点到 k 节点之间的路径个数 与 k 节点到 j 节点之间的路径个数 相乘

可以得到状态状态转移方程:

dp[i][j]=dp[i][j]+dp[i][k]×dp[k][j](1kn)

怎么判断路径个数无数呢?

显然,路径个数无数当且只当图中有环时出现。当一个节点到达自己的路径数不是 0 时,那么它就是图上环的一部分。

三、实现代码

#include <bits/stdc++.h>
using namespace std;

const int N = 30;
int m, n, t;
int dp[N][N];

void floyd() {
    for (int k = 0; k < n; k++)
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                dp[i][j] += dp[i][k] * dp[k][j]; //记录边数
}

int main() {
    while (scanf("%d", &m) != EOF) {
        int a, b;
        n = 0;
        memset(dp, 0, sizeof(dp));

        while (m--) {
            scanf("%d%d", &a, &b);
            dp[a][b] = 1;       //单向边,同时边权为1
            n = max({n, a, b}); //最大节点号
        }

        //注意有0节点,所以矩阵边长为n+1。
        n++;

        floyd();

        printf("matrix for city %d\n", t++);

        //结论:某个节点到自己的距离不为0,就是有环
        for (int k = 0; k < n; k++)
            if (dp[k][k]) { //如果存在环的话,需要特殊处理
                for (int i = 0; i < n; i++)
                    for (int j = 0; j < n; j++)
                        if (dp[i][k] && dp[k][j]) //如果有其它点i,j利用过k,则i->k,k->j都是无数条路径
                            dp[i][j] = -1;
                dp[k][k] = -1; //标识k->k也有无数条路径
            }

        //严格控制格式,输出矩阵
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++)
                if (j == 0)
                    printf("%d", dp[i][j]);
                else
                    printf(" %d", dp[i][j]);
            puts("");
        }
    }
    return 0;
}
posted @   糖豆爸爸  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2016-11-01 定时备份表
2014-11-01 下一步开发的技术点
Live2D
点击右上角即可分享
微信分享提示