任意两点都恰有一条边相连的图(任意两点都相邻)
满足任意两点都恰有一条边相连的子图,也叫团
就是最大完全子图(最大指的是点数最多)
1、最大团点的数量=补图中最大独立集点的数量
2、二分图中,最大独立集点的数量+最小覆盖点的数量=整个图点的数量
3、二分图中,最小覆盖点的数量=最大匹配的数量
4、图的染色问题中,最少需要的颜色的数量=最大团点的数量
ps:
最大团问题是NP问题
求最大团的点数
裸题
2000-ms
#include
#include
using namespace std;
const int maxm=55;
int g[maxm][maxm];//存图
int vis[maxm];//存放已经选择的点
int cnt[maxm];//cnt[i]表示用编号>=i的点能组成的最大团的点数
int ans;//答案
int n;//顶点数
bool dfs(int cur,int num){//num是已经选择的点数
for(int i=cur+1;i<=n;i++){//枚举所有编号比cur大的点作为下一个选择的点
if(cnt[i]+num<=ans){//不可能更新答案了
return 0;//剪枝
}
if(g[cur][i]){//如果和i相邻
int ok=1;//flag
for(int j=0;j<num;j++){//判断是否和所有已选中的点相邻
if(!g[i][vis[j]]){//如果有一个点不相邻则不行
ok=0;
break;
}
}
if(ok){//如果i和所有已经选中的点相邻
vis[num]=i;
if(dfs(i,num+1)){//第一次dfs成功的一定是最大的
return 1;
}
}
}
}
if(num>ans){//更新答案
ans=num;
return 1;
}
return 0;
}
void maxclique(){
ans=0;
for(int i=n;i>=1;i--){//从后往前枚举第一个选择的点
vis[0]=i;
dfs(i,1);
cnt[i]=ans;
}
printf("%d\n",cnt[1]);
}
int main(){
while(scanf("%d",&n)){
if(n==0)break;
for(int i=1;i<=n;i++){//输入图
for(int j=1;j<=n;j++){
scanf("%d",&g[i][j]);
}
}
maxclique();
}
return 0;
}
1500-ms,还没完全看懂先挂着
#include
#include
using namespace std;
const int N=60;
int G[N][N];
int n,Max[N],Alt[N][N],ans;
bool DFS(int cur,int tot){
if(cur==0) {
if(tot>ans) {
ans=tot;
return 1;
}
return 0;
}
for(int i=0;i<cur;i++) {
if(cur-i+tot<=ans)return 0;
int u=Alt[tot][i];
if(Max[u]+tot<=ans)return 0;
int nxt=0;
for(int j=i+1;j<cur;j++){
if(G[u][Alt[tot][j]]){
Alt[tot+1][nxt++]=Alt[tot][j];
}
}
if(DFS(nxt,tot+1)){
return 1;
}
}
return 0;
}
int MaxClique() {
ans=0, memset(Max,0,sizeof Max);
for(int i=n-1;i>=0;i--) {
int cur=0;
for(int j=i+1;j<n;j++){
if(G[i][j]){
Alt[1][cur++]=j;
}
}
DFS(cur,1);
Max[i]=ans;
}
return ans;
}
int main(){
while(scanf("%d",&n),n) {
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%d",&G[i][j]);
}
}
printf("%d\n",MaxClique());
}
return 0;
}
//来源:https://www.cnblogs.com/zhj5chengfeng/p/3224092.html
把给定的图用黑色和白色染色,要求黑色不能和黑色相邻且黑色的点尽可能多,
输出染色之后的黑色点数量以及黑色的点的编号(即输出方案)
黑色不能相邻且要求黑色点最多,即求最大独立集
最大独立集点数=补图的最大团点数
建补图然后求最大团就行了
题目要求输出方案,只要在每次更新最大团的时候记录方案就行了
#include
#include
#include
using namespace std;
const int maxm=105;
int g[maxm][maxm];
int group[maxm];//存最大团方案
int cnt[maxm];
int vis[maxm];
int n,m;
int ans;
bool dfs(int cur,int num){
for(int i=cur+1;i<=n;i++){
if(cnt[i]+num<=ans)return 0;
if(g[cur][i]){
int ok=1;
for(int j=0;j<num;j++){
if(!g[i][vis[j]]){
ok=0;
break;
}
}
if(ok){
vis[num]=i;
if(dfs(i,num+1)){
return 1;
}
}
}
}
if(num>ans){
ans=num;
for(int i=0;i<num;i++){//记录方案
group[i]=vis[i];
}
return 1;
}
return 0;
}
void maxclique(){
ans=0;
for(int i=n;i>=1;i--){
vis[0]=i;
dfs(i,1);
cnt[i]=ans;
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
g[i][j]=1;//默认相连,把输入的边删掉就是补图
}
}
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
g[a][b]=g[b][a]=0;//删边
}
maxclique();
printf("%d\n",ans);//输出cnt[1]也行
for(int i=0;i<ans-1;i++){
printf("%d ",group[i]);
}
printf("%d\n",group[ans-1]);
}
return 0;
}
染色题,要求相邻的点颜色不同,问最少要用多少种颜色
图的染色问题中,最少需要的颜色的数量=最大团点的数量
显然最大团中的点必须两两颜色不同
还可以用反证法证明:如果x+1个点必须染成不同的颜色,那么这x+1个点必须相互连接,而x+1个点相互连接与最大团点数为x矛盾
#include
#include
#include
using namespace std;
const int maxm=30;
int g[maxm][maxm];
int cnt[maxm];
int vis[maxm];
int n,m;
int ans;
bool dfs(int cur,int num){
for(int i=cur+1;i<=n;i++){
if(cnt[i]+num<=ans)return 0;
if(g[cur][i]){
int ok=1;
for(int j=0;j<num;j++){
if(!g[i][vis[j]]){
ok=0;
break;
}
}
if(ok){
vis[num]=i;
if(dfs(i,num+1)){
return 1;
}
}
}
}
if(num>ans){
ans=num;
return 1;
}
return 0;
}
void maxclique(){
ans=0;
for(int i=n;i>=1;i--){
vis[0]=i;
dfs(i,1);
cnt[i]=ans;
}
}
int main(){
while(scanf("%d",&n)){
if(n==0)break;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
g[i][j]=0;
}
}
for(int i=1;i<=n;i++){
char s[30];
scanf("%s",s);
int len=strlen(s);
int a=s[0]-'A'+1;
for(int j=2;j<len;j++){
int b=s[j]-'A'+1;
g[a][b]=g[b][a]=1;
}
}
maxclique();
if(ans==1){
printf("1 channel needed.\n");
}else{
printf("%d channels needed.\n",ans);
}
}
return 0;
}
给n个点,和这n个点在坐标系上的坐标
现在要选出k个点,使得这k个点中距离最近的两个点的距离最大
最大团+二分
因为是坐标系上的点的距离,所以可以看作任意两点之间都有边
即可以选出的点组成的图可以看作是完全图
开结构体,存下任意两点的的距离和两点的编号,然后排序
因为答案肯定是这些距离中的某一个,所以二分最小距离的下标
把大于这个距离的边加入图中,这时候图中的最短距离就小于dis[mid]
但是题目还有一个要求是需要k个点,如何判断是否是k个点呢?
因为可以看作是完全图,所以整个图就是一个团,最大团的点数就是图的点数,
对于每次建立的新图,跑一遍最大团
如果最大团>=k说明至少有k个点,不断二分得出答案
ps:
我直接二分答案tle,所以二分下标
因为下标是int而且数组不是很大,比较稳
#include
#include
#include
#include
#include
using namespace std;
const int maxm=55;
int g[maxm][maxm];
int cnt[maxm];
int vis[maxm];
int n,k;
int ans;
bool dfs(int cur,int num){
for(int i=cur+1;i<=n;i++){
if(cnt[i]+num<=ans)return 0;
if(g[cur][i]){
int ok=1;
for(int j=0;j<num;j++){
if(!g[i][vis[j]]){
ok=0;
break;
}
}
if(ok){
vis[num]=i;
if(dfs(i,num+1)){
return 1;
}
}
}
}
if(num>ans){
ans=num;
return 1;
}
return 0;
}
void maxclique(){
ans=0;
for(int i=n;i>=1;i--){
vis[0]=i;
dfs(i,1);
cnt[i]=ans;
}
}
int x[maxm],y[maxm];
struct Node{
int a,b;
double c;
Node(){};
Node(int aa,int bb){
a=aa;
b=bb;
double xx=x[aa]-x[bb];
double yy=y[aa]-y[bb];
c=sqrt(xx*xx+yy*yy);
}
bool operator<(const Node a){
return c<a.c;
}
}e[maxm*maxm];
int main(){
while(scanf("%d%d",&n,&k)!=EOF){
for(int i=1;i<=n;i++){
scanf("%d%d",&x[i],&y[i]);
}
int num=0;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
e[++num]=Node(i,j);
}
}
sort(e+1,e+1+num);
int l=1,r=num;
int res=1;
while(l<=r){//枚举答案的下标
int mid=(l+r)/2;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
g[i][j]=0;
}
}
for(int i=mid;i<=num;i++){
int a=e[i].a;
int b=e[i].b;
g[a][b]=g[b][a]=1;
}
maxclique();
if(ans>=k){
res=mid;
l=mid+1;
}else{
r=mid-1;
}
}
printf("%.2f\n",e[res].c);
}
return 0;
}