最大团题目

hdu1530 Maximum Clique

题意:给定一个无向图,求最大团数

分析:求最大团,也就是求一个最大的完全子图,只能靠搜索了,用一个dp数组剪枝

搜索思路倒是很清晰

一般思路:

枚举每一个点,假设选择了这个点,那么最大团组成的集合就可能是由与这个点关联的点的集合组成,接下来就是深搜选择这个集合里面的点的过程了。

剪枝:

1)重新排列了访问的顺序,按度数大的先访问

2)用一个dp[]数组,dp[i]表示i到n-1范围内的点组成的最大团数,那么dp[i-1] 的最大可能值就是 dp[i]+1了,这个可以在搜索过程用来剪枝

悲剧,怎么改都是1000+ms

View Code
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 55;
int dp[N];//dp[i] 表示 [i,n) 范围内的最大团个数
bool inset[N],g[N][N];
int n,best,ord[N],deg[N];
bool found;//记录每次搜索时是否更新
void Memcpy(bool *d,bool *s)
{
for(int i=0;i<n;i++)
d[i]=s[ord[i]];
}
bool cmp(int a,int b)
{
return deg[a]<deg[b];
}
int find(int start,bool *s)//查找第一个关联的点
{
for(int i=start+1;i<n;i++)
if(s[i])
return i;
return -1;
}
void clique(bool *s,int start,int len)
{
int first=find(start,s),i;
if(first==-1)
{
if(len>best)
{
best=len;
found=true;
}
return ;
}
bool tmp[N];
while(first!=-1)
{
if(len+n-start<=best || len+dp[first]<=best)
//已选择的点的个数+剩余点的个数<=最大可能值
//已选择的点的个数+后面的最大团数<=最大可能值
return ;
tmp[first]=false;
for(i=first+1;i<n;i++)
tmp[i]=s[i]&g[ord[first]][ord[i]];//i之后与当前集合重叠的点,只有重叠的点才又可能与已选择的点关联
clique(tmp,first,len+1);
if(found) return ;
first=find(first,s);
}
}
int main()
{
while(scanf("%d",&n)==1 && n)
{
memset(deg,0,sizeof(deg));
for(int i=0;i<n;i++)
{
ord[i]=i;
for(int j=0;j<n;j++)
{
scanf("%d",&g[i][j]);
deg[i]+=g[i][j];
}
}
sort(ord,ord+n,cmp);//按度数重新排列访问顺序
best=0;
dp[n-1]=1;
for(int i=n-2;i>=0;i--)
{
Memcpy(inset,g[ord[i]]);
found=false;
clique(inset,i,1);
dp[i]=best;
}
printf("%d\n",dp[0]);
}
return 0;
}

 pku 1419 Graph Coloring

题意:给定一个无向图,求一个最大点独立集。

分析:首先由

最大团数=补图的最大点独立集

反之亦然,也就是补图的最大团数

所以,还是直接用模板,只不过要记录一下路径

View Code
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 101;
int dp[N];
bool inset[N],g[N][N];
int n,best,ord[N],deg[N];
bool found;
int set[N],ans[N];
void Memcpy(bool *d,bool *s)
{
for(int i=0;i<n;i++)
d[i]=s[ord[i]];
}
bool cmp(int a,int b)
{
return deg[a]<deg[b];
}
int find(int start,bool *s)
{
for(int i=start+1;i<n;i++)
if(s[i])
return i;
return -1;
}
void clique(bool *s,int start,int len)
{
int first=find(start,s),i;
if(first==-1)
{
if(len>best)
{
best=len;
for(int i=0;i<best;i++)
ans[i]=set[i];
found=true;
}
return ;
}
bool tmp[N];
while(first!=-1)
{
if(len+n-start<=best || len+dp[first]<=best)
return ;
tmp[first]=false;
set[len]=ord[first];
for(i=first+1;i<n;i++)
tmp[i]=s[i]&g[ord[first]][ord[i]];
clique(tmp,first,len+1);
if(found) return ;
first=find(first,s);
}
}
int main()
{
int T,m,a,b;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
memset(deg,0,sizeof(deg));
memset(g,true,sizeof(g));
for(int i=0;i<n;i++)
{
ord[i]=i;
deg[i]=n;
}
while(m--)
{
scanf("%d %d",&a,&b);
a--;b--;
g[a][b]=false;
g[b][a]=false;
deg[a]--;
deg[b]--;
}
sort(ord,ord+n,cmp);
best=0;
dp[n-1]=1;
for(int i=n-2;i>=0;i--)
{
Memcpy(inset,g[ord[i]]);
found=false;
set[0]=ord[i];
clique(inset,i,1);
dp[i]=best;
}
sort(ans,ans+dp[0]);
printf("%d\n%d",dp[0],ans[0]+1);
for(int i=1;i<dp[0];i++)
printf(" %d",ans[i]+1);
printf("\n");
}
return 0;
}

 pku1129  Channel Allocation

还是模板题

View Code
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 30;
int dp[N];
bool inset[N],g[N][N];
int n,best,ord[N],deg[N];
bool found;
void Memcpy(bool *d,bool *s)
{
for(int i=0;i<n;i++)
d[i]=s[ord[i]];
}
bool cmp(int a,int b)
{
return deg[a]<deg[b];
}
int find(int start,bool *s)
{
for(int i=start+1;i<n;i++)
if(s[i])
return i;
return -1;
}
void clique(bool *s,int start,int len)
{
int first=find(start,s),i;
if(first==-1)
{
if(len>best)
{
best=len;
found=true;
}
return ;
}
bool tmp[N];
while(first!=-1)
{
if(len+n-start<=best || len+dp[first]<=best)
return ;
tmp[first]=false;
for(i=first+1;i<n;i++)
tmp[i]=s[i]&g[ord[first]][ord[i]];
clique(tmp,first,len+1);
if(found) return ;
first=find(first,s);
}
}
int main()
{
char str[50];
while(scanf("%d",&n)==1 && n)
{
memset(g,false,sizeof(g));
memset(deg,0,sizeof(deg));
for(int i=0;i<n;i++)
{
ord[i]=i;
scanf("%s",str);
for(int j=2;j<strlen(str);j++)
{
int t=str[j]-'A';
g[i][t]=true;
g[t][i]=true;
deg[i]++;
deg[t]++;
}
}
sort(ord,ord+n,cmp);
best=0;
dp[n-1]=1;
for(int i=n-2;i>=0;i--)
{
Memcpy(inset,g[ord[i]]);
found=false;
clique(inset,i,1);
dp[i]=best;
}
if(dp[0]==1)
printf("%d channel needed.\n",dp[0]);
else printf("%d channels needed.\n",dp[0]);
}
return 0;
}

 hdu 1045 Fire Net

题意:给定一个n*n的矩阵,'.'表示空白区,‘X’表示墙壁,求在该矩阵上放置碉堡的数量的最大值,使得所有碉堡不能相互攻击(墙壁可以挡住攻击)

分析:将矩阵上的所有点重新标号,0~n*n,同时,将所有可以直接攻击到的点连接起来,这样,题目就转变成了求一个图的最大独立集。

设墙壁的数量为x

所以最后可以放置碉堡的数量=最大独立点数-x (最大独立点数里面包含了墙壁数)

又因为最大独立数=补图的最大团数,所有就可以用最大团解决了。

View Code
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 20;
char g[5][5];
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int dp[N];
bool inset[N],mat[N][N];
int r,n,best,ord[N],deg[N],num;
bool found;
void Memcpy(bool *d,bool *s)
{
for(int i=0;i<n;i++)
d[i]=s[ord[i]];
}
bool cmp(int a,int b)
{
return deg[a]<deg[b];
}
int find(int start,bool *s)
{
for(int i=start+1;i<n;i++)
if(s[i])
return i;
return -1;
}
void clique(bool *s,int start,int len)
{
int first=find(start,s),i;
if(first==-1)
{
if(len>best)
{
best=len;
found=true;
}
return ;
}
bool tmp[N];
while(first!=-1)
{
if(len+n-start<=best || len+dp[first]<=best)
return ;
tmp[first]=false;
for(i=first+1;i<n;i++)
tmp[i]=s[i]&mat[ord[first]][ord[i]];
clique(tmp,first,len+1);
if(found) return ;
first=find(first,s);
}
}
bool check(int x,int y)
{
if(x<0||x>=r || y<0 || y>=r)
return false;
return true;
}
void build()
{
num=0;
memset(mat,true,sizeof(mat));
for(int i=0;i<n;i++)
{
deg[i]=n;
ord[i]=i;
}
for(int i=0;i<r;i++)
{
for(int j=0;j<r;j++)
{
int a=i*r+j,b;
if(g[i][j]=='X'){
num++;
continue;
}
for(int k=0;k<4;k++)
{
int x=dir[k][0]+i;
int y=dir[k][1]+j;
while(check(x,y) && g[x][y]!='X')
{
b=x*r+y;
mat[a][b]=false;
deg[a]--;
deg[b]--;
x+=dir[k][0];
y+=dir[k][1];
}
}
}
}
}
int main()
{
while(scanf("%d",&r)==1 && r)
{
for(int i=0;i<r;i++)
scanf("%s",g[i]);
n=r*r;
build();
sort(ord,ord+n,cmp);
best=0;
dp[n-1]=1;
for(int i=n-2;i>=0;i--)
{
Memcpy(inset,mat[ord[i]]);
found=false;
clique(inset,i,1);
dp[i]=best;
}
printf("%d\n",dp[0]-num);
}
return 0;
}

 

 

posted @ 2012-03-08 23:27  枕边梦  阅读(556)  评论(0编辑  收藏  举报