问题描述
蒜头君收到了一个生日礼物——一盒精美的蜡笔,这可把他高兴坏了。蒜头君在完成一道图论的题目后,拿着蜡笔想给题目上的一个无向图进行填色。无向图上一共有 n 个点,编号从 0 到 n - 1,那么该图就会有 2^n - 1个非空子图。 蒜头君想给每 i 个子图进行填色,使得任意一条边连接的两个点的颜色不同,现在他想知道给第 i 个子图填色,最少需要多少种不同的颜色,记为 s i 。蒜头君想请你帮他计算一下以下式子的结果:
输入格式
第一行输入一个整数 n(1≤n≤16),表示无向图有 n个点。
接下来输入一个n×n 的 0101 矩阵,表示无向图边的情况。如果 a[i][j] = 1,则说明 i与 j之间有边相连,如果 a[i][j] =0,则表示 i与j之间没有边相连。
输出格式
输出一行,输出上述式子的结果,结果可能会很大,输出结果对 2^{32}取余的结果即可。
样例输入
40111101111011110
样例输出
1595912448
刚看到此题,%2^32.......怎么办,学到了学到了
可用unsigned int 自然溢出模拟%2^{32}。Unsigned longlong 可用来模拟%2^{64}。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned int ui;
ui cnt=0,dp[1<<17]={0};
int minx=0x3f3f3f3f;
int n;
ui pow_x(ui a,ui b)
{
ui res=1,temp=a;
while(b){
if(b&1){
res=res*temp;
}
temp=temp*temp;
b>>=1;
}
return res;
}
int a[20][20]={{0}};
bool flag[(1<<16)+10]={0};//用来标志该组此数是否在其中
int check(int k){
memset(flag,0,sizeof(flag));//每次调用都需初始化
for(int i=0;i<n;i++){
if(k &(1<<i)){
flag[i+1]=true;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(a[i][j]&&flag[i]&&flag[j])
return 0;
}
}
return 1;
}
int main(){
char c;
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>c;
a[i][j]=c-'0';
}
}
for(int i=1;i<(1<<n);i++){
if(check(i)) dp[i]=1;
else{
dp[i]=minx;
for(int j=i;j;j=(j-1)&i){
dp[i]=min(dp[i],dp[j]+dp[j^i]);
}
}
}
int p=233;
for(int i=1;i<(1<<n);i++){
ui tt=pow_x(p,i)*dp[i];
cnt+=tt;
}
cout<<cnt<<endl;
return 0;
}