题目大意:
给出一个图,让你给一些点染成黑色,要求相连的两个点不能同时被染色,求最大的染色数量。
分析:
数据量不是很大,回溯法可以解决,不过单纯的回溯会TLE,要加上一点剪枝。
由于是递归时是从1到n,所以如果当前染色点加上剩余的点都比当前的最优答案小的话,是肯定不能构成最大解的,直接return。if(n+ans-i<Max) return;
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<iostream>
using namespace std;
int n,m;
int a[110][110];
int vis[110];
int res[110];
int Max=0;
void dfs(int ans)
{
if(ans>Max)
{
Max=ans;
memcpy(res,vis,sizeof(vis));
}
for(int i=1; i<=n; i++)
{
if(ans+n-i<Max)
return;
if(vis[i])
continue;
int ok=1;
for(int j=1; j<=n; j++)
{
if(a[i][j]&&vis[j])
{
ok=0;
break;
}
}
if(ok)
{
vis[i]=1;
dfs(ans+1);
vis[i]=0;
}
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
scanf("%d%d",&n,&m);
int p,q;
Max=0;
memset(vis,0,sizeof(vis));
memset(a,0,sizeof(a));
for(int i=1; i<=m; i++)
{
scanf("%d%d",&p,&q);
a[p][q]=1;
a[q][p]=1;
}
dfs(0);
cout<<Max<<endl;
int fi=1;
for(int i=1; i<=n; i++)
{
if(res[i])
{
if(fi)
{
cout<<i;
fi=0;
}
else
printf(" %d",i);
}
}
cout<<endl;
}
return 0;
}
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<iostream>
using namespace std;
int n,m;
int a[110][110];
int vis[110];
int res[110];
int Max=0;
void dfs(int i,int ans)
{
if(i>n&&ans>Max)
{
Max=ans;
memcpy(res,vis,sizeof(vis));
return ;
}
int ok=1;
for(int j=1; j<=n; j++)
{
if(a[i][j]&&vis[j])
{
ok=0;
break;
}
}
if(n+ans-i<Max)
return;
vis[i]=1;
if(ok)
dfs(i+1,ans+1);
vis[i]=0;
dfs(i+1,ans);
}
int main()
{
int t;
cin>>t;
while(t--)
{
scanf("%d%d",&n,&m);
int aa,bb;
Max=0;
memset(vis,0,sizeof(vis));
memset(a,0,sizeof(a));
for(int i=1; i<=m; i++)
{
scanf("%d%d",&aa,&bb);
a[aa][bb]=1;
a[bb][aa]=1;
}
dfs(1,0);
cout<<Max<<endl;
// cout<<res[1];
int fi=1;
for(int i=1; i<=n; i++)
{
if(res[i])
{
if(fi)
{
cout<<i;
fi=0;
}
else
printf(" %d",i);
}
}
cout<<endl;
}
return 0;
}
提供了两种AC的代码,大体思路都是相同的。区别是递归的方式有些不同,第一种采取了一种循环的方式,而第二种没有使用循环,直接是将点从1到n进行递归。显然第二种在时间效率上更高。
第一种是按照图的搜索进行,这样的就是n^n,因为每次都会扩展n个节点。
而第二种是按照顺序进行搜索,每次只扩展两个节点,2^n,效率高很多。
对递归的理解还是不清晰。有清晰的理解请留言指教。不胜感激。