动态规划·状压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;
}

C.涂抹果酱

posted @ 2024-12-30 16:53  还是沄沄沄  阅读(2)  评论(0编辑  收藏  举报