hdu5823 color II

Problem Description You are given an undirected graph with n vertices
numbered 0 through n-1.

Obviously, the vertices have 2^n - 1 non-empty subsets. For a
non-empty subset S, we define a proper coloring of S is a way to
assign each vertex in S a color, so that no two vertices in S with the
same color are directly connected by an edge. Assume we’ve used k
different kinds of colors in a proper coloring. We define the
chromatic number of subset S is the minimum possible k among all the
proper colorings of S.

Now your task is to compute the chromatic number of every non-empty
subset of the n vertices.

Input First line contains an integer t. Then t testcases follow.

In each testcase: First line contains an integer n. Next n lines each
contains a string consisting of ‘0’ and ‘1’. For 0<=i<=n-1 and
0<=j<=n-1, if the j-th character of the i-th line is ‘1’, then
vertices i and j are directly connected by an edge, otherwise they are
not directly connected.

The i-th character of the i-th line is always ‘0’. The i-th character
of the j-th line is always the same as the j-th character of the i-th
line.

For all testcases, 1<=n<=18. There are no more than 100 testcases with
1<=n<=10, no more than 3 testcases with 11<=n<=15, and no more than 2
testcases with 16<=n<=18.

Output For each testcase, only print an integer as your answer in a
line.

This integer is determined as follows: We define the identity number
of a subset S is id(S)=∑v∈S2v. Let the chromatic number of S be
fid(S).

You need to output ∑1<=id(S)<=2n−1fid(S)×233id(S)mod232.

状压dp。
对于每个集合,枚举他的子集,如果这个子集之间没有边的话,就可以用剩下的集合的答案+1更新答案。
任何一个集合之间是否有边可以预处理出来。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define LL long long
const LL mod=1LL<<31;
int map[20][20],n,dp[530000];
unsigned int pw[530000];
bool ok[530000];
char s[20];
int main()
{

    int i,j,k,p,q,x,y,z,S,maxsize,now,T;
    unsigned int ans=0;
    scanf("%d",&T);
    pw[0]=1;
    for (i=1;i<=529989;i++)
      pw[i]=pw[i-1]*233;
    while (T--)
    {
        scanf("%d",&n);
        memset(map,0,sizeof(map));
        for (i=0;i<n;i++)
        {
            scanf("%s",s);
            for (j=0;j<n;j++)
              map[i][j]=s[j]-'0';
        }
        maxsize=(1<<n)-1;
        memset(ok,0,sizeof(ok));
        for (S=0;S<=maxsize;S++)
        {
            ok[S]=1;
            for (i=0;i<n&&ok[S];i++)
              if (S&(1<<i))
                for (j=0;j<n&&ok[S];j++)
                  if (S&(1<<j)&&map[i][j])
                    ok[S]=0;
        }
        memset(dp,0x3f,sizeof(dp));
        dp[0]=0;
        ans=0;
        for (S=1;S<=maxsize;S++)
          for (now=S;now;now=(S&(now-1)))
            if (ok[now])
              dp[S]=min(dp[S],dp[S-now]+1);
        for (S=1;S<=maxsize;S++)
          ans+=pw[S]*dp[S];
        cout<<ans<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值