先了解一下二进制:
如果我们把二进制中的每个数看作一个状态时,则二进制只有两种状态,
0
和1
,我们用1
表示存在,0
表示不存在,给n
个数,这n
个数会组成多少种状态: 答案很明显会有2^n
个状态,这其实是高中时学的组合数学,每个位置有2
种可能,则n
个位置有2^n
种可能。2^n
个状态代表总状态量
举个栗子:
假如果有5个数,我们要把它的所有状态枚举出来,它的所有状态是32种状态,现在我把代码写下来,并用测试样例来清晰表示32种状态
讲解一下:i&(1<<j)
判断
i
的第j
位是否存在,将1
向左移动j
位:
比如:5(00101)判断它哪一位存在00101&00001
00101&00010
00101&00100
00101&01000
00101&10000
显然1 3位存在即判断正确
代码:
二进制枚举子集
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
int n=5;
for(int i=0;i<(1<<n);i++)//总状态数量
{
for(int j=0;j<n;j++)
{
if(i&(1<<j))//判断第i种状态&每一位的存在性
{
printf("1");
}
else
{
printf("0");
}
}
printf("\n");
}
}
样例输出:
看一道例题:
传送门:二进制枚举子集
题意:
T组输入
有n个砝码,给m组询问,给出n个砝码的重量
每组询问给一个物体重量,问:是否能称出这个物体是多重
题解:
很明显枚举出n个砝码的组成状态即可,例如有两个砝码 重量1 4则它的所有状态是:(0)(1)(4)(5)这四种状态,当询问物体重量为这几种的时候,则是可以得到其重量的
AC代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=25;
const int sizx=2005;
int a[maxn];
int vis[sizx];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(vis,0,sizeof(vis));
int n;
scanf("%d",&n);
for(int i=0; i<n; i++)
{
scanf("%d",&a[i]);
}
for(int i=0; i<(1<<n); i++) //枚举子集
{
int cur=0;
for(int j=0; j<n; j++)
{
if(i&(1<<j))
cur+=a[j];
}
vis[cur]=1;
for(int j=0; j<n; j++)
{
if(cur-a[j]>=0)
{
vis[cur-a[j]]=1;
//cur-=a[j];
}
}
}
int m;
scanf("%d",&m);
while(m--)
{
int wi;
scanf("%d",&wi);
if(vis[wi])
{
printf("YES\n");
}
else
{
printf("NO\n");
}
}
}
}