#10172. 「一本通 5.4 练习 1」涂抹果酱 【 三进制状态压缩 】【 方案数 】

Tyvj 两周年庆典要到了,Sam 想为 Tyvj 做一个大蛋糕。蛋糕俯视图是一个 N×M 的矩形,它被划分成 N×M 个边长为 1×1 的小正方形区域(可以把蛋糕当成 NNN 行 MMM 列的矩阵)。蛋糕很快做好了,但光秃秃的蛋糕肯定不好看!所以,Sam 要在蛋糕的上表面涂抹果酱。果酱有三种,分别是红果酱、绿果酱、蓝果酱,三种果酱的编号分别为 1,2,31,2,31,2,3。为了保证蛋糕的视觉效果,Admin 下达了死命令:相邻的区域严禁使用同种果酱。但 Sam 在接到这条命令之前,已经涂好了蛋糕第 KKK 行的果酱,且无法修改。
现在 Sam 想知道:能令 Admin 满意的涂果酱方案有多少种。请输出方案数 mod106。若不存在满足条件的方案,请输出 000。

样例输入(点击右上角复制按钮 即可复制)

2 2 
1 
2 3

样例输出

3

 

因为数据范围中M很小,可以想到是状态压缩,而且毫无疑问地会选择压缩每一行。

因为这道题中有3种需要进行区分的果酱,所以使用三进制压缩,分别用0、1、2来表示。

然后按照普通的状压DP枚举每一行,进行合法方案数上的转移即可。

并且可以考虑预处理来优化

1. 每一行总共有 3^{m}-1种状态,但是因为相同颜色的果酱不能放在相邻的位置,所以每一行的不可行状态有很多。

    e.g: m=3时,枚举了状态 111 或 000 或 222,显然,这样是没有必要的。

    这就需要预处理出一行中的可行状态。经过统计,可以发现,即便是m=5,一行中的合法状态也只有48种,姑且当作50吧。

2. 在DP时,并不需要将每一种状态对应的十进制记录出来,更省空间且更简便的做法是:在储存时编号,这样就可以在遍历上一行和当前行的状态时直接取编号  ->这也是状压DP的常见套路了吧

3. 预处理行与行之间的判断

    使得时间复杂度有很高的保证。

总体最高时间复杂度为n\times 50\times 50+50\times 50\div 2+250\times 5 (也有粗略的四舍五入啊)大概250万的样子

 

细节问题也要注意啊:在运算过程中也有溢出的可能性

#include 
#include 
#include 
#include 
#define N 10006
#define mod 1000000
using namespace std;

inline int wread(){
	char c=getchar ();int flag=1,wans=0;
	while (c<'0'||c>'9'){if (c=='-') flag=-1;c=getchar ();}
	while (c>='0'&&c<='9'){wans=wans*10+c-'0';c=getchar ();}
	return wans*=flag;
}

int n,m;

int thre[250];//3^i的值 
int lin_cal[5];//临时存储 
int cal[50][5],top_cal;//每一种合法状态的每一位的数字  合法状态用编号储存 
int pre[50];//取出合法的状态 
int bel[250];
//三进制处理
void getthree(int x){
	thre[0]=1;
	for (int i=1;i<=x;++i)
		thre[i]=thre[i-1]*3;
	for (int i=0;i

 

你可能感兴趣的:(状压DP,DP)