放牧代码和思想
专注自然语言处理、机器学习算法
    愛しさ 優しさ すべて投げ出してもいい

POJ 2441 Arrange the Bulls 题解 《挑战程序设计竞赛》

目录

POJ 2441 Arrange the Bulls

公牛队:农夫约翰有N支公牛队,分别独占M个球场中的若干个,求分配方案的个数?

3.4熟练掌握动态规划

状态压缩DP

白天写代码混饭,晚上写代码寻欢。

基本上是个带记忆的枚举,难点在对集合状态的枚举上。我第一次是这么干的:

#include <iostream>
#include <vector>
using namespace std;
#define MAX_N 20
#define MAX_M 20

vector<int> dp[2];	        // dp[now]表示满足了i的情况下剩下场地构成的集合S
vector<int> barn[MAX_N];	// 每个球队喜欢的球场

///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	int N, M;
	cin >> N >> M;
	for (int i = 0; i < N; ++i)
	{
		int P;
		cin >> P;
		barn[i].reserve(P);
		for (int j = 0; j < P; ++j)
		{
			int b;
			cin >> b; --b;
			barn[i].push_back(b);
		}
	}
	int S = (1 << M) - 1;
	for (int i = 0; i < barn[0].size(); ++i)
	{
		dp[0].push_back(S & ~(1 << barn[0][i]));
	}
	for (int i = 1; i < N; ++i)
	{
		int pre = (i - 1) & 1;
		int now = i & 1;
		dp[now].clear();
		for (int j = 0; j < barn[i].size(); ++j)
		{
			for (int k = 0; k < dp[pre].size(); ++k)
			{
				if (dp[pre][k] >> barn[i][j] & 1)
				{
					dp[now].push_back(dp[pre][k] & ~(1 << barn[i][j]));
				}
			}
		}
	}

	cout << dp[(N - 1) & 1].size() << endl;
#ifndef ONLINE_JUDGE
	fclose(stdin);
	fclose(stdout);
	system("out.txt");
#endif
	return 0;
}
///////////////////////////End Sub//////////////////////////////////

结果MLE了,而且这里肯定不能用vector的,最后vector里面都是相同的S,估计应该用map记录S和出现的次数,可以省点内存。这里如果会枚举子集的话就相当省了:

基本枚举:

枚举子集:

定义 dp[S] := 当场地集合为S时,每个球队都不冲突的情况种数。

那么 dp[1 << (第一个球队喜欢的场地编号)] = 1

递推方法是,从第i个球队开始,枚举集合{0, 1, … , M – 1}的大小为i的子集,也就是分配了i个场地,满足了i个球队,如果球队i喜欢场地j并且j不在集合S中,那么dp[S ∪ j] += dp[S]。

#include <iostream>
#include <bitset>
using namespace std;
#define MAX_N 20
#define MAX_M 20

int dp[1 << MAX_M];	        // dp[S]表示已分配场地构成的集合S时的情况种数
bool like[MAX_N][MAX_M];	// like[i][j]表示球队i喜欢场地j

///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	int N, M;
	cin >> N >> M;
	for (int i = 0; i < N; ++i)
	{
		int P; cin >> P;
		for (int j = 0; j < P; ++j)
		{
			int b;
			cin >> b; --b;
			like[i][b] = true;
		}
	}
	for (int i = 0; i < M; ++i)
	{
		if (like[0][i])
		{
			dp[1 << i] = 1;	// 将i场地分配给0号球队
		}
	}
	for (int i = 1; i < N; ++i)
	{
		for (int comb = (1 << i) - 1, x, y; comb < (1 << M); x = comb & -comb, y = comb + x, comb = ((comb & ~y) / x >> 1) | y)
		{
			// comb表示集合{0, 1, ... , M - 1}的大小为i的子集,也就是分配了i个场地,满足了i个球队
			if (dp[comb])
			{
				for (int j = 0; j < M; ++j)
				{
					if (like[i][j] && !((comb >> j) & 1))
					{
						// i需要j并且j没有被分配,此时将j分配掉
						dp[comb | (1 << j)] += dp[comb];
					}
				}
			}
		}
	}

	int result = 0;
	for (int bit = 0; bit < (1 << M); ++bit)
	{
		if (bitset<32>(bit).count() == N)
		{
			result += dp[bit];
		}
	}
	cout << result << endl;
#ifndef ONLINE_JUDGE
	fclose(stdin);
	fclose(stdout);
	system("out.txt");
#endif
	return 0;
}
///////////////////////////End Sub//////////////////////////////////
13253776 hankcs 2441 Accepted 4356K 172MS C++ 1476B 2014-08-06 03:47:55

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » POJ 2441 Arrange the Bulls 题解 《挑战程序设计竞赛》

评论 欢迎留言

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

我的作品

HanLP自然语言处理包《自然语言处理入门》