题意:
给你一个下标从\(0\) ~ \(n-1\)的\(favourite\)数组,\(favourite[i]\)表示下标\(i\)喜欢的人的下标为\(favourite[i]\),现在有张圆形的桌子让你安排这些尽量多的人去参加会议,要保证,每个人的 左右两边至少有一个是自己喜欢的人,每个人只会喜欢一个人,问最多能安排多少人参加会议。
思路:
本题可以转化为图论问题(有向图),假设一些人的喜欢的 人能形成一个环,环上的每一个人都能满足条件——旁边有至少有一个人是自己喜欢的。所以我们可以先求出图中最大环,答案可能是最大环上点的数目\(cnt\)。还有一种情况:假设两个人相互喜欢,那么可以去寻找这两个人的喜欢者的最长的链,形成的一个二元环加上两条链也能满足条件,而且多个这种形式放在一起也能满足条件,所以我们还需要统计出二元环加上两条链的所有情况的参与人数\(ans\),答案就是\(max(ans, cnt)\)。
考察算法:
最大环如何求:先用拓扑排序的思想筛选出能形成环的点,在\(dfs\)求出最大的环。
两条最长的链怎么求:可以用\(dfs\)深搜求,这样需要反向建图,这种情况是一定不会搜到环,因为反向建图,指向这个人的是喜欢者,每个喜欢者只会喜欢一个人。
代码:
const int N = 100010;
class Solution {
public:
int du[N], h[N], e[N], ne[N], idx = 0, vis[N], h1[N], e1[N], ne1[N], idx1 = 0;
bool st[N];
int dfs(int index, vector<int>&favorite)//搜最大环
{
if(!vis[index])
{
vis[index] = true;
return dfs(favorite[index], favorite) + 1;
}
return 0;
}
void add(int a,int b)//正向建图
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void add1(int a,int b)//反向建图
{
e1[idx1] = b, ne1[idx1] = h1[a], h1[a] = idx1 ++;
}
int dfs1(int x)//计算最长的链
{
int v = 0;
for(int kk = h1[x]; kk != -1; kk = ne1[kk]) v = max(v, dfs1(kk));
return v + 1;
}
int calc(int i, int j)//计算两条链的最大长度
{
int v = 0;
for(int k = h1[i]; k != -1; k = ne1[k])
{
int x = e1[k];
if(x != j)
v = max(v, dfs1(x));
}
int w = 0;
for(int k = h1[j]; k != -1; k = ne1[k])
{
int x = e1[k];
if(x != i)
w = max(w, dfs1(x));
}
return w + v;
}
int maximumInvitations(vector<int>& favorite) {
int n = favorite.size();
for(int i = 0; i < n; i ++ )
{
h[i] = -1;
h1[i] = -1;
st[i] = true;
vis[i] = false;
}
for(int i = 0; i < n; i ++ )
{
add(i, favorite[i]);
add1(favorite[i], i);
du[favorite[i]] ++;
}
queue<int>q;
for(int i = 0; i < n; i ++ )
if(!du[i])
q.push(i);
while(!q.empty())//拓扑排序筛出能形成环的点
{
int x = q.front();
st[x] = false;
q.pop();
for(int i = h[x]; i != -1; i = ne[i])
{
int y = e[i];
du[y] --;
if(!du[y]) q.push(y);
}
}
int cnt = 0;//环的最大长度
for(int i = 0; i < n; i ++ ){
if(!vis[i] && st[i])
cnt = max(dfs(i, favorite), cnt);
}
//最长的一个环的长度为cnt。
//接下来要求二元环产生的可行性方案
int ans = 0;
for(int i = 0; i < n; i ++ )
{
if(favorite[favorite[i]] == i && favorite[i] > i)//喜欢的人喜欢的人是我自己
{
ans += 2 + calc(i, favorite[i]);
}
}
return max(ans, cnt);
}
};