题目:上帝手中有 N 种世界元素,每种元素可以限制另外 1 种元素,把第 i 种世界元素能够限制的那种世界元素记为 A[i]。现在,上帝要把它们中的一部分投放到一个新的空间中去建造世界。为了世界的和平与安宁,上帝希望所有被投放的世界元素都有至少一个没有被投放的世界元素限制它。上帝希望知道,在此前提下,他最多可以投放多少种世界元素?
数据:n<=1e6
摆明了O(n)时间复杂度,可怎么做呢?
我们把这个关系看成一个环,x->a[x]表示x限制a[x]
我们可以使用贪心思想,就比如上图,我们可以确定,1号和2号一定不能投放(因为没有元素约束1,2)
利用贪心思想,如图1,3号4号是要投放的,5号不投放,(2个)
那么如果3号也不投放,让5号投放,此时,(2个)
看似一样可实际是不一样的,如果5下面还有,那么肯定是图1更好
所以这跟没有上司的舞会的区别,就是那题只有一个“上级”,很多下级。而这个有很多“上级”,却只有1个下级
那题里舍弃上司参加舞会而换来了一群下级参加舞会,所以必须要用树形dp
废话有点多,我们讲“流程”
1.拓扑排序,把所有非环的点全部删掉,在按照不要,要的顺序记录答案,
2.环计数,sum+=ct/2,解释一下,sum就是求和变量,ct就是当前环的大小,其实就是一半(向下取整)的人要,另外的人不要
这次讲的比较模糊我们上代码拆开来说
1.拓扑排序(bfs):
/*
变量说明:
in[i]:还可能可以约束i的数量(还没有确定i考虑的数量)
v[i]:是否考虑过i(不是是否选i)
ans:记录答案
*/
void bfs() {
for (int i = 1; i <= n; ++i)
if (!in[i]) q.push(i);//把无人约束的,也就是一定不要的,拉入队列
while (q.size()) {
int u = q.front(); q.pop();
//u一定不要
v[u] = 1;//表示考虑过u
if (!v[a[u]]) {//如果v[a[u]]=1,那就要多算1遍
++ans;//根据贪心,a[u]一定要选
v[a[u]] = 1;//考虑过a[u]
int x = a[a[u]];//那x一定不能依赖a[u]
if (--in[x] == 0 && !v[x])//如果没人可依赖且没考虑过
q.push(x);//加入队中
}
}
}
很清楚了吧!
再来看环考虑,这个好理解一些:
//vis[i]:i是否考虑过环
for (int i = 1; i <= n; ++i)
if (!v[i] && !vis[i]) {//如果i是环中一点且没考虑过
int t = i, ct = 0;
while (!vis[a[t]]) {//还没转完一圈
t = a[t];//往后转
vis[t] = 1;
++ct;
}
ans += ct / 2;//加一半
}
最后代码就不奉上了(避免成为三件编程员!)