题目
求最少打击的团伙数,并且使最大的团伙危险程度不超过n/2。
分析
从 n n n到 1 1 1逆向枚举,每次把点 k k k加入图中,也就是删掉 1 ∼ k − 1 1\sim k-1 1∼k−1,剩余 k ∼ n k\sim n k∼n,若最大集合点数不超过 n / 2 n/2 n/2,说明这种方案可行, k k k还能更小,一旦不满足,意味着第 k k k个点加入图中就不可行了,所以点 k k k必须得删,所以输出答案。
邻接矩阵代码(0ms 5152k)
#include <cstdio>
#include <cctype>
using namespace std;
int n,a[1001][1001],c[1001],f[1001];
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int getf(int u){return (f[u]==u)?u:f[u]=getf(f[u]);}
int main(){
n=in();
for (int i=1;i<=n;i++){
c[i]=1; f[i]=i; a[i][0]=in();
for (int j=1;j<=a[i][0];j++)
a[i][j]=in();
}
for (int k=n;k>=1;k--)
for (int i=1;i<=a[k][0];i++)
if (a[k][i]>k){//加入
int x=getf(k),y=getf(a[k][i]);
if (x!=y){//不同集合
f[y]=x;
c[x]+=c[y];//团伙
if (c[x]>n/2){//超过
printf("%d",k);
return 0;
}
}
}
}
邻接表代码(0ms 1276k)
#include <cstdio>
#include <cctype>
using namespace std;
struct node{int y,next;}e[5001];//不可能太多的
int n,m,ls[1001],c[1001],f[1001];
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int getf(int u){return (f[u]==u)?u:f[u]=getf(f[u]);}
int main(){
n=in(); int t,x,y;
for (int i=1;i<=n;i++){
c[i]=1; f[i]=i; t=in();
for (int j=1;j<=t;j++){//邻接表
e[++m].y=in();
e[m].next=ls[i];
ls[i]=m;
}
}
for (int k=n;k>=1;k--){
t=ls[k];
while (t){
if (e[t].y>k){//犯罪团伙
x=getf(k); y=getf(e[t].y);
if (x!=y){//不在集合
f[y]=x;
c[x]+=c[y];
if (c[x]>n/2){//超过
printf("%d",k);
return 0;
}
}
}
t=e[t].next;
}
}
}