动态规划·状压DP
状压DP
好像在遥远的ITbegin听过
状压DP,顾名思义压缩状态然后转移。
【YbtOj】例题
A.状态压缩
将每行的状态压为一维。设状态 \(f_{i,j}\) 表示第 \(i\) 行的状态为 \(j\) 的方案数,因为一个 \(1\) 上下左右不能有其他 \(1\) ,所以这维状态还和上一行状态 \(k\) 密切相关。所以状态转移方程即
\[f_{i,j}=\Sigma f_{i-1,k}
\]
\(j\) 可以从 \(k\) 转移来当且仅当 \(j\) 本身合法且 \(j\) 与 \(k\) 合法。最后的答案就是 \(\Sigma f_{m,j}\)
贴
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=15;
const int MOD=1e8;
int m,n,a[N];
int g[1<<15],f[N][1<<15],ans;
bool ok(int opt) { return !(opt&(opt<<1))&&!(opt&(opt>>1)); }
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>m>>n;
for (int i=1;i<=m;i++)
{
for (int j=1,k;j<=n;j++)
{
cin>>k;
a[i]=(a[i]<<1)|k;
}
}
for (int i=0;i<(1<<n);i++)
{
if (ok(i))
{
g[i]=1;
if ((i&a[1])==i) f[1][i]=1;
}
}
for (int i=2;i<=m;i++)
for (int k=0;k<(1<<n);k++)
{
if (!g[k]||(k&a[i-1])!=k) continue;
for (int j=0;j<(1<<n);j++)
{
if (!g[j]||(j&a[i])!=j) continue;
if (!(j&k)) f[i][j]=(f[i][j]+f[i-1][k])%MOD;
}
}
for (int i=0;i<(1<<n);i++) ans=(ans+f[m][i])%MOD;
cout<<ans;
return 0;
}
B.最短路径
这道题中的状态表示每个点被经过的情况。设状态 \(f_{i,j}\) 表示到达点 \(i\) 状态为 \(j\) 的最短路径,那么状态 \(j\) 一定是从挖掉一个 \(1\) 的状态 \(k\) 转移而来,转移的上一个点就是挖掉的 \(1\) 的代表点。那么状态转移方程就是
\[f_{i,j}=\Sigma(f_{k,j\ \oplus (1<<i)}+a_{k,i})
\]
贴
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=22;
int n,a[N][N];
int f[N][1<<20];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
memset(f,0x3f,sizeof f);
cin>>n;
for (int i=0;i<n;i++)
for (int j=0;j<n;j++) cin>>a[i][j];
f[0][1]=0;
for (int j=1;j<(1<<n);j++)
for (int i=0;i<n;i++)
{
if (!(j&(1<<i))) continue;
for (int k=0;k<n;k++)
{
if (j&(1<<k)) f[i][j]=min(f[i][j],f[k][j^(1<<i)]+a[k][i]);
}
}
cout<<f[n-1][(1<<n)-1];
return 0;
}