hdu 5823 color II 状压dp 图的色数

color II

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 62 Accepted Submission(s): 15

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)=vS2v . Let the chromatic number of S be fid(S).

You need to output 2n1id(S)=1fid(S)×233id(S)mod232 .

Sample Input
2
4
0110
1010
1101
0010
4
0111
1010
1101
1010

Sample Output
1022423354
2538351020

Hint

For the first test case, ans[1..15]= {1, 1, 2, 1, 2, 2, 3, 1, 1, 1, 2, 2, 2, 2, 3}

Author
学军中学

Source
2016 Multi-University Training Contest 8

Recommend
wange2014 | We have carefully selected several similar problems for you: 5831 5830 5829 5828 5827


题意:

求公式 2n1id(S)=1fid(S)×233id(S)mod232.
其中 fid(S) 为点集s二进制状压下的十进制数表示,
fid(S) 为由点集s构成的子图最少需要的颜色数,使得对每个点涂色后相邻点不重色。


解:

对于集合s,最少的涂色种数dp[s]= min{dp[s-s0]+1 };
(s0为s的非空子集,并且s0内的点互不相连,你说是不是?)

实际上就是状态转移时,每次考虑的都是一套新颜色的点集。

时间复杂度 O(n3)
证明:
O(nk=0C(n,k)2k)=O((1+2)n)=O(3n) (二项式定理)


代码:

#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<cmath>
#define ysk(x)  (1<<(x))
using namespace std;
const int INF=1e9+7;
const double eps=1e-7;
const int maxn=18;
const int maxS=262144;
typedef long long ll;
const ll mod=((ll)1<<32) ;
const ll Xmod=((ll)1<<32)-1 ;
ll pre[maxS+10] ;//pre[x]=233^(id(S))%mod
int n,ed;//点数,n个点下的最大状态
bool G[maxn+5][maxn+5];//邻接矩阵
bool no_edge_inside[maxS+5];//如果值为1 ,表示没有边使当前点集中的点相连
ll dp[maxS+5];

int lowbit(int x)
{
    return x&(-x);
}
int getId(int s)//得到单个元素的点集中元素编号
{
    int cnt=-1;
    while(s)
    {
        s>>=1;
        cnt++;
    }
    return cnt;
}
void find_no_edge_inside()//枚举点集,判断点集内的点是否有边相连。
{
    ed=ysk(n)-1;
    memset(no_edge_inside,0,(ed+1)*sizeof no_edge_inside[0]);
    no_edge_inside[0]=1;
    for(int s=1;s<=ed;s++)
    {
       int ns=lowbit(s);
       int s2=s^ns;
       int i=getId(ns);
       no_edge_inside[s]=no_edge_inside[s2];
       if(!no_edge_inside[s])  continue;
       for(int j=0;j<n;j++)  if( ysk(j)&s2 )
       {
           if(G[i][j])
           {
               no_edge_inside[s]=0;
               break;
           }
       }

    }

}
void getPre()
{
    pre[0]=1;
    for(int i=1;i<=maxS;i++)
    {
        pre[i]=(pre[i-1]*233)&Xmod;
    }
}


void solve()//状压dp
{
    dp[0]=0;
    for(int s=1;s<=ed;s++)
    {
        dp[s]=INF;
        for(int s0=s;s0;s0=(s0-1)&s)
        {
           if(no_edge_inside[s0])
            dp[s]=min(dp[s],dp[s-s0]+1);
        }
    }
    ll ans=0;
    for(int s=1;s<=ed;s++)
    {
       ans=(ans+ (dp[s]*pre[s]&Xmod))&Xmod;
    }
    printf("%lld\n",ans);

}
int main()
{
    getPre();
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        char ch;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
               scanf(" %c",&ch);
               if(ch=='0')  G[i][j]=0;
               else  G[i][j]=1;
            }
        }
        find_no_edge_inside();
        solve();
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值