http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5120
有N个人要买宠物,有M只猫和P只狗,人的编号是1~N,猫的编号是N+1~N+M,狗的编号是N+M+1~N+M+P;每个人都买一只猫和一只狗;
然后有Q条限制,表明对应编号的人和猫(过敏)或是猫和狗(打架)不能在一起,然后问有多少种购买方案使得N个人都满足,所谓的满足就是这个人买的猫和狗不打架,同时人不会对买的猫造成过敏。
dp[i][j]表示取到第i个人第j种状态时的方案数。当前猫和狗取了哪几只以二进制压缩在第二维中。
1240MS。还有10MS更优解法。
#include<bits/stdc++.h>
using namespace std;
int x[45][45];
long long dp[2][(1<<21)];
int main(){
int n,m,p,a,b,nn;
while(~scanf("%d %d %d",&n,&m,&p)){
memset(x,0,sizeof(x));
scanf("%d",&nn);
while(nn--){
scanf("%d %d",&a,&b);
x[a][b]=1;x[b][a]=1;
}
for(int i=0;i<(1<<(m+p));++i)
dp[0][i]=0;
//为什么memset(dp[0],0,(1<<(m+p)))就错了?
dp[0][0]=1;
int q=1;
for(int i=1;i<=n;i++){
for(int j=0;j<(1<<(m+p));++j)
dp[q][j]=0;
for(int j=0;j<(1<<(m+p));++j){
if(dp[1-q][j]==0)
continue;
for(int k=0;k<m;k++){
if(x[i][n+(k+1)]||(j&(1<<k))) //人猫过敏或者状态j里这只猫已经被取过了
continue;
for(int l=m;l<m+p;l++){
if(x[n+(k+1)][n+(l+1)]||j&(1<<l)||j+(1<<k)+(1<<l)>=(1<<(m+p))) //猫狗过敏或者状态j里这只狗已经被取过了
continue;
dp[q][j+(1<<k)+(1<<l)]+=dp[1-q][j];
}
}
}
q=1-q;
}
long long ans=0;
for(int j=0;j<(1<<(m+p));j++)
ans+=dp[1-q][j];
printf("%lld\n",ans);
}
return 0;
}