牛奶工厂

牛奶工厂

牛奶生意正红红火火!

农夫约翰的牛奶加工厂内有 N 个加工站,编号为 1N,以及 N1 条通道,每条连接某两个加工站。(通道建设很昂贵,所以约翰选择使用了最小数量的通道,使得从每个加工站出发都可以到达所有其他加工站)。

为了创新和提升效率,约翰在每条通道上安装了传送带。

不幸的是,当他意识到传送带是单向的已经太晚了,现在每条通道只能沿着一个方向通行了!

所以现在的情况不再是从每个加工站出发都能够到达其他加工站了。

然而,约翰认为事情可能还不算完全失败,只要至少还存在一个加工站 i 满足从其他每个加工站出发都可以到达加工站 i

注意从其他任意一个加工站 j 前往加工站 i 可能会经过 ij 之间的一些中间站点。

请帮助约翰求出是否存在这样的加工站 i。 

输入格式

输入的第一行包含一个整数 N,为加工站的数量。

以下 N1 行每行包含两个空格分隔的整数 aibi,满足 1ai,biN 以及 aibi

这表示有一条从加工站 ai 向加工站 bi 移动的传送带,仅允许沿从 aibi 的方向移动。

输出格式

如果存在加工站 i 满足可以从任意其他加工站出发都可以到达加工站 i,输出最小的满足条件的 i

否则,输出 1

数据范围

1N100

输入样例:

1 3
2 1 2
3 3 2

输出样例:

2

 

解题思路

  题目大意就是给定一颗有向树,问是否存在一个结点,使得树中的其他所有结点都可以走到这个结点。

  可以用Floyd的传递闭包算法,来判断两个点是否连通,时间复杂度为O(n3)

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 const int N = 110;
 6 
 7 bool graph[N][N];
 8 
 9 int main() {
10     int n;
11     scanf("%d", &n);
12     for (int i = 1; i <= n; i++) {
13         graph[i][i] = true;
14     }
15     
16     for (int i = 0; i < n - 1; i++) {
17         int v, w;
18         scanf("%d %d", &v, &w);
19         graph[v][w] = true;
20     }
21     
22     for (int k = 1; k <= n; k++) {
23         for (int i = 1; i <= n; i++) {
24             for (int j = 1; j <= n; j++) {
25                 // 如果graph[i][k] == true意味着可以从i到达k,同理graph[k][j],如果都为true,意味着可以从i经过k到达j
26                 graph[i][j] |= graph[i][k] & graph[k][j];
27             }
28         }
29     }
30     
31     for (int j = 1; j <= n; j++) {
32         int cnt = 0;
33         for (int i = 1; i <= n; i++) {
34             if (graph[i][j]) cnt++;
35         }
36         if (cnt == n) {
37             printf("%d", j);
38             return 0;
39         }
40     }
41     
42     printf("-1");
43     
44     return 0;
45 }
复制代码

  也可以用dfs。是否存在一个结点,使得树中的其他所有结点都可以走到这个结点,等价于是否存在一个结点,这个结点可以走到其他所有的结点。因此可以对每一个结点都进行一次dfs,如果发现某个结点可以走到其他所有的结点,那么这个结点就是要找的。时间复杂度为O(n2)

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int N = 110;
 7 
 8 int head[N], e[N], ne[N], idx;
 9 
10 void add(int v, int w) {
11     e[idx] = w, ne[idx] = head[v], head[v] = idx++;
12 }
13 
14 // 返回以src为根的子树所包含的结点个数
15 int dfs(int src) {
16     int cnt = 1;
17     for (int i = head[src]; i != -1; i = ne[i]) {
18         cnt += dfs(e[i]);
19     }
20     
21     return cnt;
22 }
23 
24 int main() {
25     memset(head, -1, sizeof(head));
26     
27     int n;
28     scanf("%d", &n);
29     for (int i = 1; i <= n - 1; i++) {
30         int v, w;
31         scanf("%d %d", &v, &w);
32         add(w, v);
33     }
34     
35     int ret = -1;
36     for (int i = 1; i <= n; i++) {
37         if (dfs(i) == n) {
38             ret = i;
39             break;
40         }
41     }
42     
43     printf("%d", ret);
44     
45     return 0;
46 }
复制代码

  下面介绍一种O(n)的解法,需要进行证明。

  首先如果有解的话,那么解必然是唯一的。假设存在一个点A,其他所有的点都可以走到A。同时存在一个不同于点A的点B,同样满足其他所有的点都可以走到B。由于这是一颗树,因此任意两个点间的路径是唯一的,由于任何一个点可以走到A,所以对于B而言,所有边的方向都是往A走的方向。又因为任何一个点可以走到B,所以A可以走到B,所以AB的边的方向都是往B走的方向。因为AB的路径是唯一的,因此可以发现A,B这条路径上的边应该具备两个方向。由于这是一颗有向树,即边的方向只有一个,这样就矛盾了。

  因此如果有解的话,那么解必然是唯一的。

  如果存在一个解的话,即存在一个点,所有的点都可以到达这个点,那么我们让这个点作为树的根节点,可以发现除了根节点外,所有点都会往上指,即存在一个父节点。又因为在一颗树中,每个点的父节点是唯一的,而且除了根节点外,每个点都会有一个父节点,因此除了根节点外,其余的点的出度一定不为0,根节点的出度一定为0(因为除了根节点外,所有点都有父节点)。

  因此我们得到有解的一个必要条件,出度为0的点有且只有一个。

  我们再证明充分性。

  我们设出度为0的点为根节点。反证法,假设出度为0的点有且只有一个会推出无解,即存在一个点不可以走到根节点。

  因此出度为0的点不止一个,矛盾了。所以出度为0的点有且只有一个可以推出有解。

  因此有解与出度为0的点有且只有一个是一个充分必要条件。

  因此我们只需要记录每一个点的出度,判断是否只有一个点的出度为0即可。

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 const int N = 110;
 6 
 7 int deg[N];
 8 
 9 int main() {
10     int n;
11     scanf("%d", &n);
12     for (int i = 0; i < n - 1; i++) {
13         int v, w;
14         scanf("%d %d", &v, &w);
15         deg[v]++;
16     }
17     
18     int cnt = 0, ret;
19     for (int i = 1; i <= n; i++) {
20         if (deg[i] == 0) {
21             cnt++;
22             ret = i;
23         }
24     }
25     printf("%d", cnt == 1 ? ret : -1);
26     
27     return 0;
28 }
复制代码

 

参考资料

  AcWing 1471. 牛奶工厂(寒假每日一题2022):https://www.acwing.com/video/3706/

posted @   onlyblues  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示