状态压缩DP
例题https://www.luogu.com.cn/problem/P1896
状态压缩DP:
1.用二进制表示状态,用十进制数存储状态;
2.用位运算筛选出合法状态;
3.用位运算判断状态转移的条件;
4.计算时每个类累加上一行的兼容类。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int f[11][121][1 << 11];
// f[i][j][k]表示第i行放了j个国王的第k中状态的方案数
int s[500];
int num[1 << 11];
void solve()
{
int n, k;
cin >> n >> k;
int cnt = 0;
for (int i = 0; i < (1 << n); i++)
{
if (!(i & (i >> 1)))
{
s[cnt++] = i; // 预处理记录合法状态
// 记录每个合法状态的国王数
for (int j = 0; j < n; j++)
{
num[i] += (i >> j & 1);
}
}
}
// 不放国王也是一种方案
f[0][0][0] = 1;
/*
第一层枚举层数
第二层枚举国王数
第三层枚举第i行的合法状态
第四层枚举第i-1行的合法状态
*/
for (int i = 1; i <= n + 1; i++)
{
for (int j = 0; j <= k; j++)
{
for (int a = 0; a < cnt; a++)
{
for (int b = 0; b < cnt; b++)
{
int c = num[s[a]];
//转移方程 第一个个数必须合法 上一个合法的位置不跟当前行合法位置矛盾
if (j >= c && !(s[a] & s[b]) && !(s[a] & (s[b] >> 1)) && !(s[a] & (s[b] << 1)))
{
f[i][j][a] += f[i - 1][j - c][b];
}
}
}
}
}
cout << f[n + 1][k][0] << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin>>t;
while (t--)
{
solve();
}
return 0;
}
例题2https://www.luogu.com.cn/problem/P1879
情况确实比上一题要少一点
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e8;
int mp[14];
int s[1 << 14];
int f[14][1 << 14];
void solve()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
int num;
cin >> num;
mp[i] = (mp[i] << 1) + num;
}
}
int cnt = 0;
for (int i = 0; i < (1 << m); i++)
{
if (!(i & (i >> 1)))
{
s[cnt++] = i;
}
}
f[0][0] = 1;
for (int i = 1; i <= n + 1; i++)
{
for (int a = 0; a < cnt; a++)
{
for (int b = 0; b < cnt; b++)
{
if ((s[a] & mp[i]) == s[a] && !(s[a] & s[b]))
{
// f[i][a] += f[i - 1][b];
f[i][a] = (f[i][a] + f[i - 1][b]) % mod;
}
}
}
}
cout << f[n + 1][0] << endl;
// int ans = 0;
// for (int i = 0; i <= cnt; i++)
// {
// ans += f[n][i];
// }
// cout << ans;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin>>t;
while (t--)
{
solve();
}
return 0;
}

浙公网安备 33010602011771号