P8756 [蓝桥杯 2021 省 AB2] 国际象棋 状压dp统计情况数的一些小理解

目录

  • 建议有状压基础再食用:
    • 本题的状态转移方程是
  • dp代码片:
  • 参考代码

建议有状压基础再食用:

n行m列 等价 n列m行 ,因为n比较小,int是32位足够了,我们用比特位统计每一行的状态。

本题的状态转移方程是

dp[h][i][j][num] = (dp[h][i][j][num] + dp[h - 1][j][ii][num - nums[i]])%mod;
h是行数,i和j表示本行状态和上一行状态,num表示个数。
nums[i]是情况为 i 时的bit位为1的数目,提前可以统计一下。
dp的值就是求的情况数。

很难理解,其实我们先不看i 和 j,只看行数和num,这才是dp的样子。
然后加上i和j状态压缩,就是状压dp了。

(动态规划是有条理的遍历,是全面覆盖的,num所有可以的情况都会遍历。本行i是0也会,所以只有前几行放棋子的,后面全是0也会遍历到的。)

dp代码片:

前一行和本行情况的比特位存在隔2的

前两行和本行情况的比特位存在隔1的情况直接略去,也就是马会互吃的情况。

//初始化
dp[0][0][0][0] = 1;//0行什么也不放。第一行肯定会摸一下,方案数是1
//

for (int h = 1; h <= m; h++)
{
	for (int i = 0; i < (1ll << n); i++)//本行
	{
		for (int j = 0; j < (1ll << n); j++)//前一行
		{
			for (int ii = 0; ii < (1ll << n); ii++)//前两行
			{
				for (int num = nums[i]; num <= k; num++)
				{
					if ((i << 2 & j) || (i >> 2 & j))
						continue;
					if ((i << 1 & ii) || (i >> 1 & ii))
						continue;

					dp[h][i][j][num] = (dp[h][i][j][num] + dp[h - 1][j][ii][num - nums[i]])%mod;

				}
			}
		}
	}
}

参考代码

int n,m,k;

int countb(int aim)
{
	int ret = 0;
	for (int i = 0; i < n; i++)
	{
		if (aim & (1ll << i))
		{
			ret++;
		}
	}
	return ret;
}

void solve()
{
	cin >> n >> m >> k;
	//n行m列  等价  n列m行
	//n列可统计状压
	vectornums(1 << n);
	for (int i = 0; i < (1ll << n); i++)
	{
		nums[i] = countb(i);
	}
	vector>>>dp(m+1, vector>>(		1ll<>(1ll << n,vector(k+1)	)  )	 );
	//第几行 本行状态 前一行状态 个数 == 方案数

	//
	dp[0][0][0][0] = 1;//0行什么也不放。第一行肯定会摸一下,方案数是1
	//

	for (int h = 1; h <= m; h++)
	{
		for (int i = 0; i < (1ll << n); i++)//本行
		{
			for (int j = 0; j < (1ll << n); j++)//前一行
			{
				for (int ii = 0; ii < (1ll << n); ii++)//前两行
				{
					for (int num = nums[i]; num <= k; num++)
					{
						if ((i << 2 & j) || (i >> 2 & j))
							continue;
						if ((i << 1 & ii) || (i >> 1 & ii))
							continue;

						dp[h][i][j][num] = (dp[h][i][j][num] + dp[h - 1][j][ii][num - nums[i]])%mod;

					}
				}
			}
		}
	}

	//后面都是0也包括了只在前几行放的。。
	//动归
	int ans = 0;
	for (int i = 0; i < (1ll << n); i++)//本行
	{
		for (int j = 0; j < (1ll << n); j++)//前一行
		{
			ans = (ans + dp[m][i][j][k]) % mod;
		}
	}
	cout << ans;
	return;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	//cin >> t;
	for (int i = 1; i <= t; i++)
	{
		solve();
	}
	return 0;
}

你可能感兴趣的:(洛谷,蓝桥杯,职场和发展,算法)