图论——拓扑排序

Wikioi 3972 妖精大作战

题目描述 Description

有N 个妖精 ,现在每一只妖精都已经把自己所有的弹幕瞄准了一只妖精(有可能是自己)。

这些妖精的能力值都非常高,所以一旦将弹幕发射出去,瞄准的妖精必死无疑。

为了使问题变得更有趣一些,⑨打算让这些妖精按照某个顺序来发射弹幕。一旦某个妖精已经被打死了,那么他将退出战斗。

可以预见到,按照不同的顺序,最后死亡的妖精数量是不一样的。

⑨想知道,死亡的妖精数量的最大值和最小值。分别是多少

输入描述 Input Description

第一行一个整数N

接下来N 行每行一个整数,第i 行的数值表示第i 只妖精瞄准的妖精是谁 ,保证这个整数在 [1, n]范围内。

输出描述 Output Description

一行两个整数,死亡的妖精数量的最大值和最小值。

样例输入 Sample Input

4

2 3 1 1

样例输出 Sample Output

3 2

数据范围及提示 Data Size & Hint

最大:按照2-1-4 的顺序发射弹幕。

代码:

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <iostream>
 6 using namespace std;
 7 const int MAXN = 1500001;
 8 int p[MAXN], n, into[MAXN];//into 出度
 9 bool vis[MAXN], aj[MAXN], die[MAXN];
10 struct Tqueue{//自定义队列
11     int l, r, data[MAXN];
12     void clear() { l = 1; r = 0; }
13     bool empty() { return l > r; }
14     void push(int t) { data[++r] = t; }
15     int pop() { return data[l++]; }
16 } Q;
17 int getmin()//最少杀死数,从出度为0开始杀别的妖精
18 {
19     int ans = 0;
20     memset(vis, 0, sizeof(vis));
21     memset(into, 0, sizeof(into));
22     Q.clear();
23     for (int i = 1; i <= n; i++) ++into[p[i]];//计算出度
24     for (int i = 1; i <= n; i++) if (into[i] == 0) Q.push(i), vis[i] = true;//出度为0进队列
25     while(!Q.empty()){
26         int x = Q.pop();//队首元素
27         if (die[x]) continue;
28         x = p[x];//找到队首的攻击对象
29         if (die[x]) continue;
30         ++ans;//如果没死,die了他
31         die[x] =true; vis[x] =true; //处理后事
32         --into[p[x]]; //他的攻击对象安全一点
33         if (into[p[x]] == 0) Q.push(p[x]), vis[p[x]] = true;//如果已经安全,进队列。
34     }
35     for (int i = 1; i <= n; i++) if (!vis[i]){//处理环
36         int t = p[i], len = 1; vis[p[i]] = true;
37         while(t != i) { ++len; t = p[t]; vis[t] = true; }
38         ans += 1 + ((len - 1) >> 1);//最少的杀死数是一半
39     }
40     return ans;
41 }
42 int getmax()//求最多杀死数
43 {
44     int ans = n;
45     memset(into, 0, sizeof(into));
46     memset(vis, 0, sizeof(vis));
47     memset(aj, 0, sizeof(aj));
48     Q.clear();
49     for (int i = 1; i <= n; i++) ++into[p[i]];//入度为0,杀不死,减去
50        for (int i = 1; i <= n; i++) if (into[i] == 0) { --ans; Q.push(i); vis[i] = true; }
51     while(!Q.empty()){
52         int x = Q.pop();
53         --into[p[x]]; aj[p[x]] = true;
54         if (into[p[x]] == 0) { Q.push(p[x]); vis[p[x]] = true; }
55     }
56     for (int i = 1; i <= n; i++) if (!vis[i]){
57         int t = p[i]; bool jc = !aj[i]; vis[i] = true;
58         while(t != i) { vis[t] = true; jc &= !aj[t]; t = p[t];}//处理环,
59         if (jc && (!(i == p[i]))) --ans;//一个环中只有一个活着的,减去。
60     }
61     return ans;
62 }
63 int main()
64 {
65     scanf("%d", &n);
66     for (int i = 1; i <= n; i++) scanf("%d", &p[i]);
67     printf("%d %d\n", getmin(), getmax());
68 }

时间: 2024-12-13 19:00:10

图论——拓扑排序的相关文章

HDU 2647 Reward(图论-拓扑排序)

Reward Problem Description Dandelion's uncle is a boss of a factory. As the spring festival is coming , he wants to distribute rewards to his workers. Now he has a trouble about how to distribute the rewards. The workers will compare their rewards ,a

图论-拓扑排序详解

拓扑排序(topsort)详解 这篇随笔就信息学奥林匹克竞赛中图论的一个知识点--拓扑排序进行讲解.拓扑排序的内容比较基础,只要求读者学习过并了解信息学中图的相关定义和一些专业名词,但是拓扑排序的变形题目比较多,希望读者在看完本随笔后认真体会练习,掌握拓扑排序. 上课! 拓扑排序的定义 顾名思义,这是一种排序,确切地说,是一种图上排序,在一张有向无环图(注解:有向无环图即很多参考书和题解中所说的DAG)上进行排序,把其中的所有节点排成一个序列,使得图中的任意一对有边相连的节点(u,v)u要出现在

图论-拓扑排序

拓扑排序(Topological sort) 拓扑排序是对有向无环图(DAG)顶点的一种排序,它使得如果存在u, v的有向路径,那么满足序中u在v前.拓扑排序就是由一种偏序(partical order)得到的一个全序(称为拓扑有序).偏序满足自反性,反对称性,传递性的序. 拓扑排序的思路很简单,就是每次任意找一个入度为0的点输出,并把这个点以及与这个点相关的边删除,实际算法中,用一个队列实现. 算法: 1.把所有入度为0的点入队Q; 2.若队Q非空,则点u出队,输出u,否则转4; 3.把所有与

【图论】拓扑排序应用

拓扑排序虽是一种排序,但是它跟平时所接触的sort或者qsort不同,排序的意义不同.拓扑排序针对有向无回路图(DAG)而言的,不应用与存在回路的有向图. [图论]广度优先搜索和深度优先搜索 有说到了BFS和DFS,拓扑排序是DFS的一个应用. 有向无回路图能说明事件的发生的先后的顺序.比如穿衣服,士兵排队等.一个具体的例子,有N个物体,下面给出物体的重量比较,比如(a,b)表示a比b重等等,问已给出的条件是否会矛盾?其实就是判断用所给条件所组织的一个图中是否会存在环? 在DFS中加入时间戳,完

图论知识整理(2) 拓扑排序、最短路、最小生成树

===================================== 发现以前图论学的很不好,什么都不会,现在开始整理图论知识了 作者就是个蒟蒻,ORZ各位大神们 ===================================== 定义:对一个有向无环图(Directed Acyclic Graph,简称DAG)进行拓扑排序,将图中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若<u,v> ∈E(G),则u在线性序列中出现在v之前. 说的好像很有道理,然而我并没有看懂它在

数据结构:图论:拓扑排序! 两种方法!

拓扑排序:(1)由偏序变成全序的过程!直观的说,偏序指集合中仅有部分成员之间可比较!而全序指集合中全体成员之间均可比较! (2)将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前. 数据结构中进行拓扑排序的方法: 方法一: (1)在有向图中选一个没有前驱的顶点且输出之! (2)从图中删除该顶点和所有以它为尾的弧. (3)重复上述两部,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止.后一种情况说明有向图中存在环! 代码: #

洛谷P1137 旅行计划 拓扑排序 图论

洛谷P1137 旅行计划 拓扑排序   图论在拓扑排序中把每个点能够浏览的点加上去 但是这样会有重复 因为我们要求一个点向前多能浏览的点 所以我们只要求连向这个点中能向前浏览的点数最多的点这一路就是能浏览的最多的点 然后这个点就相当于是拓扑排序中使该点的入度为 0 的那个点用那个点来更新当前点就行了 1 #include <bits/stdc++.h> 2 #define For(i,j,k) for(int i=j;i<=k;i++) 3 #define LL long long 4

图论基础——邻接链表存图+拓扑排序

邻接链表存图,在这里其实是用数组进行模拟的 又叫做链式存储法,本来是要用链表实现的,但大多数情况下只需要用数组模拟即可 例: u(边的起点) v(边的终点) w(边的权值) 4 2 1 1 2 3 1 4 1 1 5 2 4 3 4 2 3 1 话不多说,直接上代码 for(int i=1;i<=m;i++) { scanf("%d%d%d",&u1,&v1,&w1); e[i].u =u1;//赋给第i条边的起点 e[i].v =v1;//赋给第i条边的

图论之拓扑排序 poj 2367 Genealogical tree

题目链接 http://poj.org/problem?id=2367 题意就是给定一系列关系,按这些关系拓扑排序. #include<cstdio> #include<cstring> #include<queue> #include<vector> #include<algorithm> using namespace std; const int maxn=200; int ans; int n; int in[maxn]; //记录入度