题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1742
题意描述:一家公司,每个程序员都有自己唯一崇拜的大神(next);公司主管想用一个算法将这些程序员分成若干项目组,算法描述如下:
1.从剩下未分配的程序员中随机选出一个,标记为current程序员;
2.新建一个项目,将current分配进该项目;
3.若current有崇拜的大神,并且那个大神还没被分配,将大神分配进current所属项目组,并将大神变为current,重复3;否则回到1;
求按照这个算法所需的项目组数量的最大值和最小值;
思路大致是:入度为0的遍历 + 完全独立环的个数 = min; 节点数n - 形成环的节点个数 + 环数 = max;
代码中有几点细节需要说明:
1.step表示遍历节点的顺序,可在遍历判断出环时(遇到本轮遍历过的节点即可判断是环)用当前step-节点中的step得到环长度;
2.circle变量用于储存是在哪轮遍历中首次被遍历的点,用于对1的思路剪枝;
3.代码分成两个过程:遍历入度为0的点顺便找一下其中的环,遍历独立环;
AC代码:
//#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>
using namespace std;
struct Node{
bool visit;
int in, next, step, circle;
Node() :circle(-1), visit(false), in(0), next(-1){}
};
void func(){
int n, single, bCircle, cCircle, cnt, step;
scanf("%d", &n);
vector<Node>v(n + 1);
for (int i = 1; i <= n; i++){
scanf("%d", &v[i].next);
v[v[i].next].in++;
}
single = bCircle = cCircle = cnt = step = 0;
for (int i = 1; i <= n; i++){
if (v[i].in == 0 && v[i].visit == false){
single++;
int src = i;
while (true){
v[src].step = step++;
v[src].visit = true;
v[src].circle = i;
src = v[src].next;
if (src == -1)break;
v[src].in--;
if (v[src].visit == false)continue;
else if (v[src].circle == i){
cnt += step - v[src].step;
bCircle++;
}
break;
}
}
}
for (int i = 1; i <= n; i++){
if (v[i].visit == false){
int src = i; cCircle++;
while (true){
v[src].visit = true;
v[src].step = step++;
src = v[src].next;
if (v[src].visit){
cnt += step - v[src].step;
break;
}
}
}
}
printf("%d %d\n", single + cCircle, n - cnt + bCircle + cCircle);
}
int main(){
//freopen("out.txt", "w", stdout);
//freopen("in.txt", "r", stdin);
func();
}