状态压缩DP解POJ3254-Corn Fields

本文深入探讨了状态压缩动态规划(DP)的概念及其在解决特定问题中的应用,通过实例解析了如何利用状态压缩技术简化状态管理和状态转移过程。详细介绍了状态数组的构建、状态转移规则以及代码实现细节,旨在帮助读者理解并掌握状态压缩DP的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文内容转自:http://blog.youkuaiyun.com/harrypoirot/article/details/23163485?utm_source=tuicool&utm_medium=referral

对状态压缩DP有了初步的认识,自己做个记录,方便以后回忆时查看。有些自己当时不太好理解的地方加上注释。想从0开始看的童鞋先看上面的链接。

先贴代码,这是poj上的一道题的题解:http://poj.org/problem?id=3254 

先把自己的几点理解写在这里,大家可以先看下面代码,不明白的地方再翻回来看。

1. 状态数组state长度我设为378。是因为题目中要求N<=12,则令N=12,调用init()函数一算就可知top=377,最多有378个状态数,并且我把下标都改为从0开始了,强迫症啊。

2. 关于statte数组,当有N列数时(全是0或1,当成二进制数看),最大就是N个1,也就是(1<<N) - 1。这些个数中,有很多是相邻位都为1的,要剔除,比如3(11),state数组只保存了合符要求的。

3. cur[i]保存的为什么是第i行的逆,而不直接保存原数。cur表示的是实际的01数,state保存的是所有可能取的数。现在就是要判断每一个可能取的合理的数,是否满足当前牧场块分布(cur值)的要求。比如state取101时,如果人家牧场cur是110,肯定不行。二者怎么进行比较判断呢?直接相与,等于0肯定不可行,但不等于0不一定可行。这里有个逻辑就是cur必须把state的所有1覆盖(可以多出),才是可行的。如果将cur取逆,则cur为1的位是不可行的,相与等于1说明将1放在不该放的地方,肯定不可行;整个值相与为000说明不该放的没放,可以放的放不放无所谓了,都行的。这种做法很巧妙,背后的数学原理是什么,是什么逻辑运算?麻烦知道的告诉我。

4. 后面没什么好说的了。填dp[i][k]的时候是三层循环而不是二层。对于第i行来说,第k个状态取值不仅要和当前实际值cur[i]比较,比较可行后还要和上一行所有状态比较呢。

#include <cstdio>
#include <cstring>
using namespace std;

#define mod 100000000
int M, N, top = 0;
//top表示每行最多的状态数

int state[378];
//state存放每行所有的可行状态(即没有相邻的状态

int dp[20][378];
//dp[i][j]:对于前i行数据,每行有前j种可能状态时的解
int cur[20];
//cur[i]表示的是第i行整行的情况

inline bool ok(int x) {	//判断状态x是否可行
	if (x&x << 1)	return false;//若存在相邻两个格子都为1,则该状态不可行
	return true;
}
void init() {			//遍历所有可能的状态
	top = 0;
	int total = 1 << N; //遍历状态的上界
	for (int i = 0; i < total; ++i) {
		if (ok(i))state[top++] = i;
	}
}
inline bool fit(int x, int k) { //判断状态x 与第k行的实际状态的逆是否有‘重合’
	if (x&cur[k])return false; //若有重合,(即x不符合要求)
	return true;  //若没有,则可行
}
/*
此处,注意要用相反存储的数据来判断,
因为若10101001是一种可行状态,则可知101001也可行(是前者的一部分)
这时x即为10101001,cur[k]为10110,x&cur[k]=0,即符合条件
*/
int main() {
	freopen("in.txt", "r", stdin);
	while (scanf("%d%d", &M, &N) != EOF) {
		init();
		memset(dp, 0, sizeof(dp));
		for (int i = 0; i < M; ++i) {
			cur[i] = 0;
			int num;
			for (int j = 0; j < N; ++j) {  //输入时就要按位来存储,cur[i]表示的是第i行整行的情况,每次改变该数字的二进制表示的一位
				scanf("%d", &num);  //表示第i行第j列的情况(0或1)
				if (num == 0) //若该格为0	
					cur[i] += (1 << (N - j -1)); //则将该位置为1(注意要以相反方式存储,原因参看line34-36
			}
		}
		for (int i = 0;i < top;i++) {
			if (!(state[i]&cur[0])) {  //判断所有可能状态与第一行的实际状态的逆是否有重合
				dp[0][i] = 1;  //若第1行的状态与第i种可行状态吻合,则dp[0][i]记为1
			}
		}

		/*
		状态转移过程中,dp[i][k] =Sigma dp[i-1][j] (j为符合条件的所有状态)
		*/
		for (int i = 1; i < M; ++i) {  //i索引第2行到第M行
			for (int k = 0; k < top; ++k) { //该循环针对所有可能的状态,找出一组与第i行相符的state[k]
				if (!(state[k] & cur[i]))	//某状态符合第i行实际情况
				{
					for (int j = 0; j < top; ++j) { //找到state[k]后,再找一组与第i-1行符合,且与第i行(state[])不冲突的状态state[j]
						if (!(state[j] & cur[i - 1]) && !(state[k] & state[j]))		//符合第i-1行实际,并且i-1行和i行不冲突
							dp[i][k] = (dp[i][k] + dp[i - 1][j]) % mod;				//上一行中状态'j'下的情况累加到本行状态‘k'上
					}
				}
			}
		}
		int ans = 0;
		for (int i = 0; i < top; ++i) { //累加最后一行所有可能状态的值,即得最终结果!
			ans = (ans + dp[M-1][i]) % mod;
		}
		printf("%d\n", ans);
	}
	return 0;
}
2. POJ-1185,炮兵阵地。地址: http://poj.org/problem?id=1185 题解如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 101;
const int M=11;
const int MAXS = 61;
char matrix[N][M];
int dp[N][MAXS][MAXS];
int state[MAXS];
int num[MAXS];
int cur[N];
int top = 0, n = 0, m = 0;

int getnum(int i) //求某状态二进制数1的个数
{
    int ret = 0;
    while(i)
    {
        i &= i - 1;
        ++ret;
    }
    return ret;
}
void initstate()
{
    top = 0;
	int total = 1<<m;
    for(int i = 0; i < total; ++i)
    {
        if(!(i&(i<<1)) && !(i&(i<<2)))
        {
            state[top] = i;
            num[top++] = getnum(i);
        }
    }
}
int main()
{
	freopen("in.txt", "r", stdin);
    int i = 0, j = 0, k = 0, t = 0;
    while(EOF != scanf("%d%d", &n, &m))
    {
        for(i = 0; i < n; ++i)
        {
            cur[i] = 0;
            scanf("%s", matrix[i]);
            for(j = 0; j < m; ++j)
            {    
                if('P' == matrix[i][j])
                {
                    cur[i] += (1<<(m-j-1));
                }
            }
        }
        for(i = 0; i < n; ++i)
            for(j = 0; j < MAXS; ++j)
                for(k = 0; k < MAXS; ++k)
                    dp[i][j][k] = -1;
        initstate();
        for(i = 0; i < top; ++i)
            if(!(state[i]&(~cur[0])))
                dp[0][i][0] = num[i];
       //dp[i][j][k] = max{dp[i][j][k], dp[i-1[k][t]]+num[j]}
       for(i = 1; i < n; ++i)
       {
           for(j = 0; j < top; ++j)// state of row i
           {
               if((~cur[i])&state[j])
                   continue;
               for(k = 0; k < top; ++k)//state of row i-1
               {
                   if((~cur[i-1])&state[k])
                       continue;
                   if(state[j]&state[k])
                       continue;
                   for(t = 0; t < top; ++t)//state of row i-2
                   {
                     if(state[j]&state[t]) //不用再判断state[k]与state[t]了,求i-1行时已经成立
                         continue;
                     if(-1 == dp[i-1][k][t])
                         continue;
                     dp[i][j][k]=max(dp[i][j][k], dp[i-1][k][t]+num[j]);
                   }
               }
           }
       }
       int ret = 0;
       for(i = 0; i < top; ++i)
       {
           for(j = 0; j < top; ++j)
           {
               if(dp[n-1][i][j]>ret)
                   ret = dp[n-1][i][j];
           }
       }
       printf("%d\n", ret);
    }
    return 0;
}




标题基于PHP + JavaScript的助眠小程序设计与实现AI更换标题第1章引言介绍助眠小程序的研究背景、意义,以及论文的研究内容和创新点。1.1研究背景与意义阐述助眠小程序在当前社会的重要性和应用价值。1.2国内外研究现状分析国内外在助眠小程序方面的研究进展及现状。1.3论文研究内容与创新点概述论文的主要研究内容和创新之处。第2章相关理论基础介绍助眠小程序设计与实现所涉及的相关理论基础。2.1PHP编程技术阐述PHP编程技术的基本概念、特点和在助眠小程序中的应用。2.2JavaScript编程技术介绍JavaScript编程技术的核心思想、作用及在小程序中的实现方式。2.3小程序设计原理讲小程序的设计原则、架构和关键技术。第3章助眠小程序需求分析对助眠小程序进行详细的需求分析,为后续设计与实现奠定基础。3.1用户需求调研用户需求调研的过程和方法,总结用户需求。3.2功能需求分析根据用户需求,分析并确定助眠小程序的核心功能和辅助功能。3.3性能需求分析明确助眠小程序在性能方面的要求,如响应速度、稳定性等。第4章助眠小程序设计详细阐述助眠小程序的设计方案,包括整体架构、功能模块和界面设计。4.1整体架构设计给出助眠小程序的整体架构设计思路和实现方案。4.2功能模块设计详细介绍各个功能模块的设计思路和实现方法。4.3界面设计阐述助眠小程序的界面设计风格、布局和交互设计。第5章助眠小程序实现与测试讲助眠小程序的实现过程,并进行详细的测试与分析。5.1开发环境搭建与配置介绍开发环境的搭建过程和相关配置信息。5.2代码实现与优化详细阐述助眠小程序的代码实现过程,包括关键技术的运用和优化措施。5.3测试与性能分析对助眠小程序进行全面的测试,包括功能测试、性能测试等,并分析测试结果。第6章结论与展望总结论文的研究成果,展望未来的研究方向和应用前景。6.1研究成果总结概括性地总结论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值