版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kidsummer/article/details/82147746
题目大意:有N头牛,M个谷仓。每头牛都有自己喜欢呆的谷仓。每个谷仓只能安排一头牛。问一共有多少种分法
思路:
我们用 dp[i][j] 来代表 前 i 头牛,j 的状态,有多少种放法。
dp[i][j] = dp[i][j] + dp[i-1][k] k 属于 1 ~ (1 << m)-1
我们可以发现, 第 i 状态只和 i-1 的状态有关 ,所以可以滚动数组。
最后我们统计答案的时候, 枚举 1 到 (1 << m) -1 每一种状态,不断加和,统计有多少种放法。
更新答案的时候,直接加起来。是因为当前的状态有可能已经有放法了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <algorithm>
#define mem(x,v) memset(x,v,sizeof(x))
#define go(i,a,b) for (int i = a; i <= b; i++)
#define og(i,a,b) for (int i = a; i >= b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
bool mp[25][25];
int dp[2][1 << 21];
int main(){
int ans = 0,n,m;
scanf("%d%d",&n,&m);
go(i,1,n){
int op,x;
scanf("%d",&op);
go(j,1,op) scanf("%d",&x),mp[i][x] = 1;
}
mem(dp,0); dp[0][0] = 1;
go(i,1,n){
int now = i%2;
int pre = (i - 1) % 2;
go(j,0,(1 << m)-1){
if (dp[pre][j]){//必须是前面的状态存在值,就是有马。
go(k,1,m){
if (mp[i][k] && !(j&(1<<(k-1)))){ //可以放这个马,正好这个位置还没有放。
dp[now][j | (1 <<(k-1))] += dp[pre][j];
}
}
}
}
mem(dp[pre],0);
}
go(i,0,(1<<m)-1) //每个状态有可能有不同放法。
ans += dp[n%2][i];
printf("%d\n",ans);
return 0;
}