题目大意:
有N个学生,N个主题。每个学生喜欢N个主题里面的某些主题。(N<=20)
问有多少种选法可以让每个学生都学到自己喜欢的主题。
用记忆化搜索会超时到死的。毕竟操作系统对于递归会不断进行入栈出栈操作,状态dp才是正解。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MAXN 20
#define LL long long
using namespace std;
int p[MAXN+1][MAXN];
int l[MAXN+1][190000], sz[MAXN+1];
LL f[2][1<<MAXN];
int main()
{
int maxs,s,t;
int i,j,k;
int ind1,ind2;
int cas, n, up=1<<MAXN;
for (int i=0; i<up; ++i)
{
int cnt=0;
for (j=i; j; j>>=1)
if (j&1) ++cnt;
l[cnt][sz[cnt]++]=i;
}
scanf("%d", &cas);
while (cas--)
{
scanf("%d", &n);
for (i=1; i<=n; ++i)
for (j=0; j<n; ++j)
scanf("%d", &p[i][j]);
ind1=0;
ind2=1;
f[0][0]=1;
for (i=1,maxs=1<<n; i<=n; ++i)
{
for (j=0,s=l[i][j]; j<sz[i]&&l[i][j]<maxs; s=l[i][++j])
{
f[ind2][s]=0;
for (k=0,t=1; k<n; ++k,t<<=1)
if (p[i][k]&&(s&t)) f[ind2][s]+=f[ind1][s^t];
}
ind2=!ind2;
ind1=!ind1;
}
printf("%lld\n", f[ind1][(1<<n)-1]);
}
return 0;
}