Timus Online Judge 1742 Team building

来源:互联网 发布:mac文本编辑器 编辑:程序博客网 时间:2024/06/08 10:41

题目大意:一个公司有N个程序猿,每个程序猿心目中都有一个greatest programmer。现在公司需要组队,如果没有小组,则在还没有被分组的程序猿中任选一个建立一个新的小组,否则就将小组内所有人心目中的greatest programmer拉进小组,直到再没有人可以进来。求分得的最少的和最多的组数。
再理一下思绪,如果一个人已经属于某个小组,而他自己心目中的greatest programmer还没有被分组,那么就可以将这个人拉进自己的小组。如果所有的分组都不能拉人了,那么在剩下的程序猿中,我们就用任意的一个程序猿来新建小组,这个选择不同,最后的结果也是不同的。而且要注意,自己是别人心目中的greatest programmer,但是自己不能反向选择,意思是这是条单向边。

说了单向边,我们就先来建图。比如这组测试数据:N = 6; 2, 4, 4, 2, 6, 5.建图之后就是这样!这里写图片描述

是否看出点什么? 最少的分组数mn = 入度为0的节点个数 + 单独的环的个数,那么这里的mn = 2 + 1。
最大的分组数mx怎么求呢?仔细观察,你会发现,在一个环中不论你首先选谁来建立分组,那么整个环中的程序猿都会被拉进分组。那么我们把所有的环都独立出来,共有M个环,kj(1 <= j <= M)个人组成1个环,那么mx = N - sum(kj) + M = N - sum(kj - 1)。注意,我的样例中环有两个,分别是2、4和5、6,所以最后的mx = 4。

大概的思路就是这样,或许会有人会像我一样,会对是否有环环相扣这种情况产生疑问。不过仔细思考就会发现,根本不会有这种情况,因为每个人只会有一个greatest programmer。是这个隐藏的条件让这道题目变得简单的。

关于时间复杂度上面,我们只要保证每个节点只会被访问1遍,那么我们就能轻易过关。

奉上代码,请多指教。

#include <iostream>#include <cstdio>#include <vector>using namespace std;int arr[100001], val[100001];vector<int> tmp;int dfs(int i, int v) {    val[i] = v;    int j = arr[i];//下一个节点    int ans;    if (val[j] >= val[i]) ans = 0;//一条路上不产生环的情况    else if (val[j]) ans = val[i] - val[j];//产生了环    else ans = dfs(j, v + 1);//环未封闭,即时这条路还没有走到尽头    val[i] = 100001;//回溯的时候把这个节点标记为走过了    return ans;}int main() {    int N;    scanf("%d", &N);    for (int i = 1; i <= N; ++i) {        scanf("%d", arr + i);        val[arr[i]] += 1;//i的greatest programmer是arr[i],即这条边从i指向arr[i],则arr[i]的入度+1    }    int mx = N, mn = 0;    for (int i = 1; i <= N; ++i) {//tmp存放入度为0的节点的编号        if (!val[i]) tmp.push_back(i);    }    memset(val, 0, sizeof val);    mn += tmp.size();    for (int i = 0; i < tmp.size(); ++i) {        mx -= dfs(tmp[i], 1);    }    for (int i = 1; i <= N; ++i) {//处理剩余的单独的环        if (!val[i]) {            mx -= dfs(i, 1);            mn += 1;        }    }    cout << mn << " " << mx << endl;    return 0;}
0 0
原创粉丝点击