POJ2441--Arrange the Bulls(状态压缩。)

版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/82147746