ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

牛奶工厂

2022-04-04 18:04:32  阅读:234  来源: 互联网

标签:结点 牛奶 加工 int 出度 工厂 include 节点


牛奶工厂

牛奶生意正红红火火!

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

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

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

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

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

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

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

输入格式

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

以下 $N−1$ 行每行包含两个空格分隔的整数 $a_{i}$ 和 $b_{i}$,满足 $1 \leq a_{i},b_{i} \leq N$ 以及 $a_{i} \ne b_{i}$。

这表示有一条从加工站 $a_{i}$ 向加工站 $b_{i}$ 移动的传送带,仅允许沿从 $a_{i}$ 到 $b_{i}$ 的方向移动。

输出格式

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

否则,输出 $−1$。

数据范围

$1 \leq N \leq 100$

输入样例:

1 3
2 1 2
3 3 2

输出样例:

2

 

解题思路

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

  可以用$Floyd$的传递闭包算法,来判断两个点是否连通,时间复杂度为$O \left( n^{3} \right)$。

  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 \left( n^{2} \right)$。

  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 \left( n \right)$的解法,需要进行证明。

  首先如果有解的话,那么解必然是唯一的。假设存在一个点$A$,其他所有的点都可以走到$A$。同时存在一个不同于点$A$的点$B$,同样满足其他所有的点都可以走到$B$。由于这是一颗树,因此任意两个点间的路径是唯一的,由于任何一个点可以走到$A$,所以对于$B$而言,所有边的方向都是往$A$走的方向。又因为任何一个点可以走到$B$,所以$A$可以走到$B$,所以$A$到$B$的边的方向都是往$B$走的方向。因为$A$间$B$的路径是唯一的,因此可以发现$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/

标签:结点,牛奶,加工,int,出度,工厂,include,节点
来源: https://www.cnblogs.com/onlyblues/p/16099840.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有