【bzoj4296】【PA2015】【Mistrzostwa】【bfs+dfs】

Description

给定一张n个点m条边的无向图,请找到一个点数最多的点集S,满足:
1.对于点集中任何一个点,它至少与d个点集中的点相邻。
2.仅保留点集中的点后,剩下的图连通。

Input

第一行包含三个正整数n,m,d(2<=n<=200000,1<=m<=200000,1<=d<n),分别表示点数,边数以及度数限制。
接下来m行,每行包含两个正整数a,b(1<=a,b<=n,a不等于b),表示a点和b点之间有一条边。

Output

若无解,输出NIE。
否则第一行输出一个正整数k,表示你找到的点数最多的点集S的点数。
第二行输出k个正整数,按升序依次输出点集中的点的编号,若有多组解,输出任意一组。

Sample Input

4 4 2
1 2
2 3
3 4
4 2

Sample Output

3
2 3 4
题解:
把所有度数小于d的点都删掉,然后在剩下的图中找一个最大联通块即可。
bfs+dfs;
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 200010
#define M 200010
using namespace std;
int point[N],next[M<<1],cnt,n,m,x,y,k,d[N],q[N],f[N],a[N],ans,s[N];
struct use{int st,en;}e[M<<1];
void add(int x,int y){
  next[++cnt]=point[x];point[x]=cnt;
  e[cnt].st=x;e[cnt].en=y;d[x]++;
}
void dfs(int x){
  f[x]=1;
  for (int i=point[x];i;i=next[i])
   if (!f[e[i].en]){
     s[++s[0]]=e[i].en;
     dfs(e[i].en);
   }
}
int main(){
  scanf("%d%d%d",&n,&m,&k);
  for (int i=1;i<=m;i++){
    scanf("%d%d",&x,&y);
    add(x,y);add(y,x);
  }	
  int h(0),t(0);
  for (int i=1;i<=n;i++) if (d[i]<k) q[++t]=i,f[i]=1;
  while (h<t){
    int u=q[++h];
    for (int i=point[u];i;i=next[i]){
      d[e[i].en]--;
      if (d[e[i].en]<k&&!f[e[i].en]) 
	    q[++t]=e[i].en,f[e[i].en]=1;
    }
  }
  for (int i=1;i<=n;i++)
    if (!f[i]){
      s[0]=0;
      s[++s[0]]=i;dfs(i);  
      if (s[0]>ans){
        ans=s[0];
        for (int j=1;j<=ans;j++)
          a[j]=s[j];
      }
	}
  if (ans==0){cout<<"NIE"<<endl;return 0;}
  sort(a+1,a+ans+1);cout<<ans<<endl;
  for (int i=1;i<=ans-1;i++)
    printf("%d ",a[i]);
  printf("%d",a[ans]);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值