题意:
给出一个无向图,判断每个子图的最小染色(使每条边连接不同颜色的点,所用最少的颜色数量)
状态压缩DP ,预处理出所有两两都无边相连的状态(独立集), 假设图B 是这种状态,且是A 的子集 ,则 DP[A] = min(DP[A -- B] +1 ) (A- B就是A 去掉B 的补集合
现在问题就是如何找出 图的子集 ,这里用到了
for(int j= n; j; j= (j-1)&n)
下面的这段代码就是求 n 的子集
#include<bits/stdc++.h>
using namespace std;
int v[20];
int main()
{
int n;
scanf("%d",&n);
int f =n;
int k = 0;
while(f)
{
v[++k] = f%2;
f/=2;
// k++;
}
for(int i = k;i>=1;i--)
printf("%d",v[i]);
cout<<endl;
for(int j= n; j; j= (j-1)&n)
{
int g= j;
int cot = 0;
memset(v,0,sizeof(v));
while(g)
{
v[++cot] =g%2;
g>>=1;
}
for(int x = k; x>=1; x--)
{
printf("%d",v[x]);
}
cout<<endl;
}
}
</pre><pre code_snippet_id="1827428" snippet_file_name="blog_20160813_7_7686513" name="code" class="cpp">#include<bits/stdc++.h>
using namespace std;
const int N = 20;
long long dp[1<<N];
char s[N][N];
int vis[(1<<N)];
long long f[1<<N];
long long inf = 10000000;
const long long mod = (1LL<<32);
void init()
{
f[0] = 1;
for(int i = 1; i<=(1<<18); i++)
{
f[i] = f[i-1]*233LL%mod;
}
}
long long min(long long a,long long b)
{
if(a>b)
a= b;
return a;
}
int main()
{
int T;
scanf("%d",&T);
init();
while(T--)
{
int n;
scanf("%d",&n);
for(int i = 0; i<n; i++)
{
scanf("%s",s[i]);
}
memset(vis,0,sizeof(vis));
for(int i = 1; i<(1<<n); i++)
{
for(int j = 0; j<n&&!vis[i]; j++)
{
if(i&(1<<j))
{
for(int k = 0; k<n&&!vis[i]; k++)
{
if(i&(1<<k))
{
if(s[j][k]=='1')//两点之间有边
{
vis[i]= 1;
}
}
}
}
}
}
dp[0]=0;
long long ans = 0;
for(int i = 1; i<(1<<n); i++)
{
dp[i] = inf;
for(int j = i; j; j = (j-1)&i)//产生 i 的子集
{
if(!vis[j])
dp[i] = min(dp[i],dp[i^j]+1);//由于i是从小到大此时 i^j 状态已经是最优解
}
ans+=(dp[i]*f[i]);
ans%=mod;
}
cout<<ans<<endl;
}
return 0;
}