D. Graph Subset Problem 思维 + 优先队列
题目大意:
给你一张图,n个点,m条边,给定k,问能否找到下面两种图中任意一种。
- 找一个大小是k的集合,这k个点两两相连
- 找一个非空集合,里面每一个点都有至少k个点和他相连。
如果能找到第一种输出格式:
第一行输出2,第二行k个数表示这个k个点,顺序随意输出。
如果可以找到第二种输出格式:
第一行输出1和集合的大小len,第二行len个数,表示这个集合
第一种和第二种集合没有优先级,输出任意一个答案即可。
如果都没有找到则输出-1。
题解:
优先队列解决
思路很简单,第二个图的找法:
- 首先先把所有小于k条边的删去,删去的同时,他连向的点的入度都-1
- 一种重复第一个步骤,最后如果剩下一个非空集合就是答案
第二个图:
- 先把所有小于k-1的边都删去,删去的同时,他连向的点入度都-1
- 如果存在一个点度数是k-1,那么就要讨论这个点能否作为答案,k*k的判断
写起来很麻烦,写了一个线段树。。。然后wa了
看到刘雨小姐姐的一个挺简洁的写法。
- 优先队列存放所有度数小于等于k-1的所有的点
- 如果度数==k-1,则讨论能否作为第一个的答案,可以则直接break
- 如果没有找到答案,那么这个点要把他连向的所有的点的度数都-1,并把这个点标记为已经访问过,重复第二个步骤
最后如果中途break了,那么就直接输出第一种答案即可,如果没有,那么判断最后是否还剩下点未被访问,如果有那么就说明这里的每一个点的度数都大于等于k,所以就找到第二种图的答案了,否则输出-1
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int in[maxn];
bool vis[maxn];
struct node{
int u;
node(int u=0):u(u){}
bool operator<(const node&a)const{
return in[a.u]<in[u];
}
};
vector<int>G[maxn];
priority_queue<node>que;
void init(int n){
while(!que.empty()) que.pop();
for(int i=1;i<=n;i++) {
in[i] = 0,vis[i] = false;
G[i].clear();
}
}
void add(int u,int v){
G[u].push_back(v);
G[v].push_back(u);
}
vector<int>a;
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
init(n);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
in[u]++,in[v]++;
// printf("")
}
if(1ll*k*(k-1)>2*m){
printf("-1
");
continue;
}
for(int i=1;i<=n;i++) {
// printf("in[%d]=%d
",i,in[i]);
if(in[i]<k) que.push(i);
sort(G[i].begin(),G[i].end());
}
int ok = 0,now = n;
while(!que.empty()){
int u = que.top().u;que.pop();
// printf("u = %d vis[%d]=%d in[%d]=%d
",u,u,vis[u],u,in[u]);
if(vis[u]) continue;
if(in[u]==k-1){
a.clear();
int len = G[u].size();
for(int i=0;i<len;i++){
int v = G[u][i];
if(vis[v]) continue;
a.push_back(v);
}
ok = u;
len = a.size();
for(int i=0;i<len&&ok;i++){
for(int j=i+1;j<len&&ok;j++){
int l = a[i],r = a[j];
if(!binary_search(G[l].begin(),G[l].end(),r)){
ok = 0;
break;
}
}
}
}
if(ok) {
a.push_back(u);
break;
}
int len = G[u].size();
for(int i=0;i<len;i++){
int v = G[u][i];
if(vis[v]) continue;
in[v]--;
if(in[v]<k) que.push(v);
}
vis[u] = true;
now--;
}
if(ok){
printf("2
");
for(int i=0;i<k;i++){
printf("%d ",a[i]);
}
printf("
");
}
else if(now){
printf("1 %d
",now);
for(int i=1;i<=n;i++){
if(vis[i]) continue;
printf("%d ",i);
}
printf("
");
}
else printf("-1
");
}
return 0;
}