版权声明:转载标注来源喔~ https://blog.csdn.net/iroy33/article/details/89892865
题意:n头牛m个牛栏,每个牛栏只能有一头牛,每头牛只能被放在自己喜欢的牛栏里,问有多少种方法
思路:一开始想开dp[1<<n][1<<m]来标记,发现开不下思路就端了orz
_builtin_popcount()计算二进制中多少个1,采用的是查表的方法
说到底,这个函数到底有什么实际用处呢?当然有了,使用一个二进制数字表示一个集合的时候,枚举一个组合(子集),需要判断这个数字里面的 1 的个数是不是和子集的大小相等。这种方法通常是属于暴力法,如果不是使用二进制数字表示集合,很可能就计算超时了。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int INF=0x3f3f3f3f;
int dp[1<<21];
int mp[25][25];
int n,m;
//1<<20 1e6
void solve()
{
dp[0]=1;
for(int i=0;i<n;++i) //按顺序安置牛
{
for(int j=0;j<(1<<m);++j)
{
if(__builtin_popcount(j)==i)
{
for(int k=0;k<m;++k)
{
if(!(j&(1<<k))&&mp[i][k]) //k栏还是空的并且牛i喜欢k
{
dp[j|(1<<k)]+=dp[j]; //栏数由少变多,所以状态0~(1<<m)-1
}
}
}
}
}
int ans=0;
for(int i=0;i<(1<<m);++i)
if(__builtin_popcount(i)==n)
ans+=dp[i];
cout<<ans<<endl;
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;++i)
{
int num;
cin>>num;
for(int j=0;j<num;++j)
{
int x;
cin>>x;
mp[i][x-1]=1; //这样的话第xxx个牛栏对应的二进制位就是1<<xxx
}
}
solve();
}