Time Limit: 4000MS | Memory Limit: 65536K | |
Total Submissions: 4234 | Accepted: 1612 |
Description
Input
Output
Sample Input
3 4
2 1 4
2 1 3
2 2 4
Sample Output
4
题意:有n头牛,m个牛圈,需要给每头牛分配一个合适的牛圈。每头牛都想一个人在一个牛圈里,且都有心中指定的牛圈。 现在给出这些牛心中想住进的牛圈。 每行先给出P,后面有P个数字,表示第i头牛想住进的牛圈的编号。 问需要满足以上条件的分配方法有多少种?
题解:状压dp,在递推式过程中我们把每个牛圈有没有住进牛看成一个状态,一共有(1<<m)-1个状态。 dp[i][j]表示第i头牛在第j种状态下有多少种分配方式。 那么状态转移方程为 dp[i][j] = sigma(dp[i-1][k]) k表示牛圈入住的状态,从0到(1<<m)-1 因为dp[20][1<<20]会超内存,这里要用滚动数组。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxm 22 int dp[2][1<<20],a[maxm][maxm]; //dp[i][j]表示第i头在第j中状态下的方法数,a[i][j]表示第i头牛能在第j个牛圈里 int main() { int n,m,k,i,j,cnt,num; while(scanf("%d%d",&n,&m)!=EOF) { memset(a,0,sizeof(a)); for(i=1;i<=n;++i) { scanf("%d",&cnt); while(cnt--) { scanf("%d",&num); a[i][num-1]=1;//为了方便运算把牛圈的编号变为从0~m-1 } } memset(dp,0,sizeof(dp)); dp[0][0]=1; for(i=1;i<=n;++i) { for(j=0;j<(1<<m);++j) { if(dp[1-(i&1)][j])//表示前一头牛在j状态下有值 { for(k=0;k<m;++k) { if(a[i][k]&&(j!=(j|(1<<k))))//表示第i头牛能放进k位置且k位置没有其他的牛 dp[i&1][j|(1<<k)]+=dp[1-(i&1)][j]; } } } memset(dp[1-(i&1)],0,sizeof(dp[1-(i&1)])); } int ans=0; for(i=1;i<(1<<m);++i)//统计所有结果 ans+=dp[n&1][i]; printf("%d\n",ans); } return 0; }