ural 1742. Team building

题目链接: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();

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值