【2021集训队出题】交朋友

【2021集训队出题】交朋友

by AmanoKumiko

Description

给出一个图,构造尽量少的团,使得团中边覆盖了整个图

Input

第一行两个数\(n,m\),表示点数和边数

接下来\(m\)行,每行两个数\(x,y\),表示一条边

Output

第一行一个数\(k\),表示团的数量

接下来\(k\)行输出团

Sample Input

4 5
1 2
1 3
2 3
2 4
3 4

Sample Output

2
3 1 2 3
3 2 3 4

Data Constraint

\(n\le10^3\)

Solution

对于前两个点:手算

第三个点:由于给出的是二分图,所以团数就是边数

接下来开始动脑子了

1.我会暴力!(跑不动

2.我会随机!(分不多

3.一直贪心地找最大团!期望得分\(28\)

4.其它算法(估价函数、乱搞……)期望得分\(???\)

一种可行做法:

我们从始至终维护一个团的集合\(A\),保证它们中的边覆盖了整个图

那么依次执行以下操作:

(1)对于每个团,考虑每个点,若不在团内且可加入就放进去

(2)对于每个团,尝试删去,若仍覆盖了图,就删去

(3)对于每个团,考虑团中每个点,尝试删去,若仍覆盖了图,就删去

可以发现,\(|A|\)是不上升的

为了保证能弄出解,对枚举顺序随机打乱以下

对所有测试点运行一段时间(2h)即可

Code

#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define random(x) rand()%(x)
#define N 1010
#define M 1000010

int n,m,f[N][N],t[N][N],p[M],sz,x[M],y[M],vis[M],T,pnum[N];
vector<int>C[M],c[M];

int main(){
	freopen("friends10.in","r",stdin);
	scanf("%d%d",&n,&m);
	F(i,1,m){
		scanf("%d%d",&x[i],&y[i]);
		f[x[i]][y[i]]=f[y[i]][x[i]]=1;
		t[x[i]][y[i]]++;t[y[i]][x[i]]++;
		C[i].push_back(x[i]);
		C[i].push_back(y[i]);
	}
	sz=m;
	srand(time(0));
	F(i,1,n)p[i]=i;
	int lst=sz;
	while(sz>8486){
		srand(time(0));
		srand(rand());
		random_shuffle(p+1,p+n+1);
		F(j,1,sz) F(i,1,n){
			bool flag=1;
			for(auto px:C[j]){
				if(!f[px][p[i]]||px==p[i]){flag=0;break;}
			}
			if(flag){
				for(auto px:C[j]){
					t[px][p[i]]++;t[p[i]][px]++;
				}
				C[j].push_back(p[i]);
			}
		}
		random_shuffle(C+1,C+sz+1);
		F(i,1,sz)vis[i]=0;
		F(i,1,sz){
			bool flag=1;
			for(auto px:C[i]){
				for(auto py:C[i]){
					if(px==py)continue;
					t[px][py]--;
					if(t[px][py]<1)flag=0;
				}
			}
			if(flag)vis[i]=1;
			else{
				for(auto px:C[i]){
					for(auto py:C[i]){
						if(px==py)continue;
						t[px][py]++;
					}
				}
			}
		}
		F(j,1,sz)if(!vis[j]){
			random_shuffle(C[j].begin(),C[j].end());
			F(i,0,C[j].size()-1){
				bool flag=1;
				for(auto px:C[j]){
					if(px==C[j][i])continue;
					t[px][C[j][i]]--;t[C[j][i]][px]--;
					if(t[px][C[j][i]]<1||t[C[j][i]][px]<1)flag=0;
				}
				if(flag){
					swap(C[j][i],C[j][C[j].size()-1]);
					C[j].pop_back();
				}
				else{
					for(auto px:C[j]){
						if(px==C[j][i])continue;
						t[px][C[j][i]]++;t[C[j][i]][px]++;
					}
				}
				if(C[j].size()<1)vis[p[j]]=1;
			}
		}
		int tail=0;
		F(i,1,sz)if(!vis[i])c[++tail]=C[i];
		sz=tail;
		F(i,1,sz)swap(C[i],c[i]);
		if(sz<lst)printf("%d\n",sz);
		lst=sz;
	}
	freopen("friends10.out","w",stdout);
	printf("%d\n",sz);
	F(i,1,sz){
		printf("%d ",C[i].size());
		for(auto px:C[i])printf("%d ",px);
		printf("\n");
	}
	return 0;
}
上一篇:Linux 常用指令


下一篇:Linux下aria2详细配置,以及接管浏览器下载项