[状压矩阵优化DP]花园

题目

题解

我就是个辣鸡,状压都没看出来,只会打dfs
对于m<=5,肯定考虑状压
令C为1,P为0
那么二进制状态最多也就11111,十进制的31,数组不大,可以过80
令dp[i][s]表示序列长度为i,最后m位状态为s的方案数,肯定可以通过dp[i][k]转移过来
至于k,s能否进行转移,我们先进行dfs预处理出所有合法情况,且两个状态能否转移。用vis数组标记。
之后枚举起点即前m个的情况,进行dp即可。
需要注意的是这个序列是环形的,因而我们需要多搞一个m,那么最后我们回到了初始的状态
朴素DP代码如下

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<set>
#include<ctime>
using namespace std;
const int mod = 1e9 + 7;
long long n,dp[100005][35],ans;
int m,k,a[6],sum[10];
bool vis[65][65],ok[65];
void check(){
	int x = 0,y = 0;
	for (int i = 1;i <= m;i ++)
		x = x * 2 + a[i];
	for (int i = 2;i <= m + 1;i ++)
		y = y * 2 + a[i];
	for (int i = 1;i <= m + 1;i ++)
		sum[i] = sum[i - 1] + a[i];
	for (int i = m;i <= m + 1;i ++)
		if (sum[i] - sum[i - m] > k)
			return ;
	vis[x][y] = 1,ok[x] = 1,ok[y] = 1;
}
void work(int x){
	memset(dp,0,sizeof(dp));
	dp[m][x] = 1;
	for (int i = m + 1;i <= n + m;i ++)
		for (int j = 0;j < (1 << m);j ++)
			for (int k = 0;k < (1 << m);k ++)
				if (vis[j][k])
					dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;
	ans = (ans + dp[n + m][x]) % mod;
}
void dfs(int step){
	if (step > m + 1){
		check();
		return ;
	}
	a[step] = 1;
	dfs(step + 1);
	a[step] = 0;
	dfs(step + 1);
}
int main(){
	scanf ("%lld%d%d",&n,&m,&k);
	dfs(1);
	for (int i = 0;i < (1 << m);i ++)
		if (ok[i])
			work(i);
	printf("%lld
",ans);
}

但是n太大了,我们搞不动,但我们会发现对于每一个状态,更新它的状态其实是一定的,且每一个状态都更新了n次。因而我们将vis数组自乘n次,加上能回到初始状态的方案数即可。是一个矩阵优化DP的做法

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<set>
#include<ctime>
using namespace std;
const int mod = 1e9 + 7;
struct matrix{
	long long x[65][65];
	matrix(){
		memset(x,0,sizeof(x));
	}
	matrix operator * (const matrix &rhs)const {
		matrix tmp;
		for (int i = 0;i <= 31;i ++)
			for (int j = 0;j <= 31;j ++)
				for (int k = 0;k <= 31;k ++)
					tmp.x[i][j] = (tmp.x[i][j] + (x[i][k] * rhs.x[k][j]) % mod) % mod;
		return tmp;
	}
}vis;
long long n,ans;
int m,k,a[6],sum[10];
bool ok[65];
void check(){
	int x = 0,y = 0;
	for (int i = 1;i <= m;i ++)
		x = x * 2 + a[i];
	for (int i = 2;i <= m + 1;i ++)
		y = y * 2 + a[i];
	for (int i = 1;i <= m + 1;i ++)
		sum[i] = sum[i - 1] + a[i];
	for (int i = m;i <= m + 1;i ++)
		if (sum[i] - sum[i - m] > k)
			return ;
	vis.x[x][y] = 1,ok[x] = 1,ok[y] = 1;
}
/*void work(int x){
	memset(dp,0,sizeof(dp));
	dp[m][x] = 1;
	for (int i = m + 1;i <= n + m;i ++)
		for (int j = 0;j < (1 << m);j ++)
			for (int k = 0;k < (1 << m);k ++)
				if (vis[j][k])
					dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;
	ans = (ans + dp[n + m][x]) % mod;
}*/
void dfs(int step){
	if (step > m + 1){
		check();
		return ;
	}
	a[step] = 1;
	dfs(step + 1);
	a[step] = 0;
	dfs(step + 1);
}
matrix qkpow(matrix x,long long y){
	matrix ans;
	for (int i = 0;i <= 31;i ++)
		ans.x[i][i] = 1;
	while (y){
		if (y & 1)
			ans = ans * x;
		x = x * x;
		y /= 2;
	}
	return ans;
}
int main(){
	scanf ("%lld%d%d",&n,&m,&k);
	dfs(1);
	vis = qkpow(vis,n);
	for (int i = 0;i < (1 << m);i ++)
		if (ok[i])
			ans = (ans + vis.x[i][i]) % mod;
	printf("%lld
",ans);
}
原文地址:https://www.cnblogs.com/lover-fucker/p/13566647.html