链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
“--------------拉帮结伙------------------”
某星光小学近期发现许多学习喜欢成为类似江湖上的帮派,现在老师对这个问题开始着手解决了。。
已知某个班级有nnn个学生,现在给出mmm对关系,如果某两个学生直接或者间接存在关系,我们便认为他们处于同一个帮派中(自己也是一个帮派)。老师们为了防止帮派太过团结, 将进行kkk次抓人,每抓走一个人,所以与这个人有直接或者间接的关系都将消失。
现在向你询问,每次老师抓走一个人之后,班级当前存在多少个帮派?
输入描述:
第一行两个正整数n,m(1≤n≤104,1≤m≤2×105)n, m(1 \leq n \leq 10^4, 1 \leq m \leq 2 \times 10^5)n,m(1≤n≤104,1≤m≤2×105), 分别表示某个班级里学生的个数和学生直接关系的数量。 首先输入nnn个学生的姓名。 接下来mmm行,每行两个字符串s1,s2(∣s1∣,∣s2∣≤10)s1, s2(|s1|, |s2| \leq 10)s1,s2(∣s1∣,∣s2∣≤10),表示两名具有直接关系的学生姓名。 接下来输入一个正整数k(1≤k≤n)k(1 \leq k \leq n)k(1≤k≤n), 表示老师抓走学生的次数。 随后kkk行,每行一个字符串sss, 表示老师本次抓走的学生姓名。 数据保证nnn个学生姓名不同,同时抓走的kkk个学生姓名不同,且都是本班级同学姓名。
输出描述:
第一行输出没有抓人之前,班级的帮派数量。 接下来kkk行,每行输出抓走当前这个学生之后,班级的帮派数量。
示例1
输入
复制6 10 Lily Maximilian Ava Christopher Zoey Ethan Lily Maximilian Ava Christopher Zoey Ethan Maximilian Ava Ava Zoey Maximilian Ethan Lily Ava Lily Christopher Ethan Lily Christopher Ethan 4 Zoey Maximilian Lily Christopher
6 10 Lily Maximilian Ava Christopher Zoey Ethan Lily Maximilian Ava Christopher Zoey Ethan Maximilian Ava Ava Zoey Maximilian Ethan Lily Ava Lily Christopher Ethan Lily Christopher Ethan 4 Zoey Maximilian Lily Christopher
输出
复制1 1 1 1 2
1 1 1 1 2
示例2
输入
复制8 13 a b c d e f g h a b b g g f f a a g b c c d d e e f h b h c h g d g 5 b g d f h
8 13 a b c d e f g h a b b g g f f a a g b c c d d e e f h b h c h g d g 5 b g d f h
输出
复制1 1 1 2 3 3
1 1 1 2 3 3
做法
本来想用并查集写,但是在不知道怎么实现,就用bfs暴力了,结果超时了。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10,M=2e5+10;
int n,m,k,ans;
unordered_map<string,int> id;
unordered_map<int,string> name;
vector<int> g[N];
int vis[N],dis[N],sc[N];
queue<int> q;
int sign;
void bfs(int x){
q.push(x);
while(q.size()){
int tmp=q.front();
q.pop();
if(dis[tmp]||sc[tmp]) continue;
dis[tmp]=1;
for(auto u:g[tmp]){
if(dis[u]||vis[u]||sc[u]) continue;
vis[u]=1;
q.push(u);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
string s;
cin>>s;
id[s]=i;
name[i]=s;
}
for(int i=1;i<=m;i++){
string a,b;
cin>>a>>b;
g[id[a]].push_back(id[b]);
g[id[b]].push_back(id[a]);
}
scanf("%d",&k);
for(int i=1;i<=n;i++){
if(vis[i]) continue;
bfs(i);
ans++;
}
cout<<ans<<endl;
while(k--){
string s;
ans=0;
cin>>s;
sc[id[s]]=1;
for(int i=1;i<=n;i++) vis[i]=0,dis[i]=0;
for(int i=1;i<=n;i++){
if(vis[i]||sc[i]) continue;
bfs(i);
ans++;
}
cout<<ans<<endl;
}
}
正解
实际上还是用并查集,只不过我们对删除操作倒着实现,把要删掉的人不做合并,算出集合个数。然后再一个个合并要删掉的人。
但是吧,实现过程有出了点错,导致又超时了。具体如下。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10,M=2e5+10;
int n,m,k;
unordered_map<string,int> id;//名字对应的序号
unordered_map<int,string> name;//序号对应的名字
vector<int> g[N];//每对关系
vector<int>ans,sc;//记录答案和删除的人
int fa[N];
unordered_map<int,int> mp;//记录是否是要删掉的人
int getfa(int x){
if(x==fa[x]) return x;
return fa[x]=getfa(fa[x]);
}
void setfa(int x,int y){
fa[getfa(x)]=getfa(y);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
string s;
cin>>s;
id[s]=i;
name[i]=s;
fa[i]=i;
}
for(int i=1;i<=m;i++){
string a,b;
cin>>a>>b;
g[id[a]].push_back(id[b]);
g[id[b]].push_back(id[a]);
}
scanf("%d",&k);
for(int i=1;i<=k;i++){
string s;
cin>>s;
sc.push_back(id[s]);
mp[id[s]]=1;
}
//先算出没有被删除的人的集合数
for(int i=1;i<=n;i++){
if(mp[i]) continue;
for(auto u:g[i]){
if(mp[u]) continue;
if(getfa(i)!=getfa(u))
setfa(i,u);
}
}
unordered_map<int,int> e;
for(int i=1;i<=n;i++){
if(count(sc.begin(),sc.end(),i)) continue;
e[getfa(i)]++;
}
ans.push_back(e.size());
int res=e.size();
for(int i=k-1;i>=0;i--){
set<int> s;
for(auto u:g[sc[i]]){
if(count(sc.begin(),sc.end(),u)) continue;
s.insert(getfa(u));
if(getfa(u)!=getfa(sc[i]))
setfa(sc[i],u);
}
sc.pop_back();
res-=s.size()-1;
ans.push_back(res);
// for(auto u:g[sc[i]]){
// if(count(sc.begin(),sc.end(),u)) continue;
// if(getfa(u)!=getfa(sc[i]))
// setfa(u,sc[i]);
// }
// sc.pop_back();
//每次都要算集合数,超时了!!!
// for(int i=1;i<=n;i++){
// if(count(sc.begin(),sc.end(),i)) continue;
// e[getfa(i)]++;
// }
// ans.push_back(e.size());
// e.clear();
}
for(int i=k;i>=0;i--){
cout<<ans[i]<<endl;
}
}