给你一个n个点的图,问每个子集的最小色数
题解:每个子图的染色问题,可以看成是找每个子图的独立集问题(两两顶点互不相邻的图叫独立集),可以先枚举每个状态,然后枚举每个顶点,表示以这个顶点为起始的子图,再枚举每个顶点,表示这个顶点和上面那个顶点所构成的图是一个独立集,预处理出所有非法的状态,即他们之间有边。
首先预处理出每个点集的子集是否为独立集.
然后令
dp[S]
表示点集
S
的导出子图的最小着色数,若其不是独立集,则:
dp[S]=minS′⊆S{dp[S′]}+1 ,其中 S∖S′ 为独立集.
因为::如果一个点集是独立集那么这些点可以只染一个颜色,那么我们就能直接枚举独立集子集然后从另一半子集+1继承过来最小染色数啦。
令dp[s]为子图S已经染色的最少染色数,那么有dp[s]=min(dp[s],dp[s^s0]+1)其中s0是S的子集,并且内部没有变,也就是s0是一个可以染成同一种颜色的结点集
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define nl n<<1
#define nr (n<<1)|1
using namespace std;
typedef long long ll;
char mapp[20][20];
int vis[1<<19];
unsigned int ans[1<<19];
int main()
{
int t;scanf("%d",&t);
while(t--)
{
int n;scanf("%d",&n);
int up=1<<n;
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++)
scanf("%s",mapp[i]);
for(int m=1;m<up;m++)
{
for(int i=0;i<n;i++)
{
if((m>>i)&1)
{
for(int j=0;j<n;j++)
{
if(((m>>j)&1)&&mapp[i][j]=='1')
{
vis[m]=1;break;//有边,不是独立子集
}
}
if(vis[m])
break;
}
}
}
memset(ans,0x3f3f3f3f,sizeof(ans));
ans[0]=0;
for(int m=1;m<up;m++)
{
for(int i=m;i;i=(i-1)&m)
{
if(!vis[i])//如果不是独立集,就找他的独立子集,+1种颜色就可染完
{
ans[m]=min(ans[m],ans[m^i]+1);
}
}
}
unsigned int bei=1,res=0;
for(int i=1;i<up;i++)
{
res+=ans[i]*(bei*=233);
}
printf("%u\n",res);
}
return 0;
}