[SCOI2005]互不侵犯KING
[SCOI2005]互不侵犯KING
https://ac.nowcoder.com/acm/problem/20240
状压dp
思路很明显,但是实现起来对我来说真难。
这里我就解读一下代码
首先我们定义了一个dp数组
dp[i][j][k]
表示,第i个状态,再第j行,之后要放置k个国王
从第一行开始遍历
我们定义状态为,如果该位为1那么这一行中,我们在这里放置了一个国王
否则我们不放置国王
我们可以先跑一下,预先把所有满足条件的状态都筛选出来
即,01间隔的二进制串,筛选函数pre(),判断非常简单
我们将筛选过的状态存在s数组里
然后下一步,我们开始枚举状态
我们从第一行开始枚举,然后枚举这一行的状态,枚举这一行之后要放置的国王
然后我们要进行状态转移了!!
我们同样开始枚举所有的状态,然后判断两个状态是否冲突。判断冲突的方法和pre()
中相似
ok
注意初始化dp[1][0][0]=1
ok
#include<iostream> #include<algorithm> #include <ctime> using namespace std; typedef long long ll; typedef pair<int, int> pii; int N, K; ll f[1 << 10][12][12 * 12]; int s[1 << 10]; int s0 = 0; void pre() { for (int i = 0;i < (1<<N);++i) { if (i & (i << 1))continue; s[++s0] = i; } } void dp() { f[1][0][0] = 1; for (int i = 1;i <= N;++i) { for (int t = 1;t <= s0;++t) { for (int k = __builtin_popcount(s[t]);k <= K;++k) { for (int j = 1;j <= s0;++j) { if ((s[j] & s[t]) || ((s[t] << 1) & s[j]) || ((s[t] >> 1) & s[j]))continue; f[t][i][k] += f[j][i - 1][k - __builtin_popcount(s[t])]; } } } } ll ans = 0; for (int i = 1;i <= s0;++i)ans += f[i][N][K]; cout << ans << endl; } int main() { ios::sync_with_stdio(0); while (cin >> N >> K)pre(), dp(); }