POJ 2411 状压dp

题目大意:

用 1*2 或者2 *1的木板填满 h*w的长方形,问总共有多少种填充方法

直接dfs会超时,因为后面答案甚至爆了int,直接搜,肯定也是long long 的时间复杂度

这里我们将当前位置没放置任何木板为 0 , 如有放置则看为 1

每次通过当前行 i 的状态 old 找到下一行 i + 1 所有满足当前行状态的状态 new , 将 dp[i+1][new] += dp[i][old]

这个new的状态一定会在old状态为 0 的位置为1 , 因为上一行为 0 , 说明是留给竖木板的, 那么新一行的对应位置必然放了竖直木板

总状态为cnt , 那么至少要保证 new 状态中 包含了 ~old&cnt 这个状态中的所有1

但是我们要找到所有可以对应old状态的new状态,那么就dfs搜可以在转换状态后加横木板的所有情况即可

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5
 6 int h,w,cnt;
 7 long long ans,add;
 8 long long dp[12][1<<12];
 9
10 void dfs(int k , int state , int i)
11 {
12     if(k > w){
13         dp[i+1][state] += add; //所有包含一开始传入状态的放置横木板后的状态
14         return;
15     }
16     if(k+1 <= w && !(state & (1<<k)) && !(state & (1<<k-1))){
17         dfs(k+2 , state | (1<<k-1) | (1<<k) , i);
18     }
19     dfs(k+1 , state , i);
20 }
21
22 int main()
23 {
24
25    // freopen("test.in","rb",stdin);
26     while(scanf("%d%d",&h,&w)!=EOF){
27         if(h == 0 && w == 0) break;
28         if((h&1) && (w&1)){
29             puts("0");
30             continue;
31         }
32         cnt = (1<<w)-1;
33         memset(dp,0,sizeof(dp));
34         add = 1;
35         dfs(1,0,0);
36         for(int i=1 ; i<h ; i++){
37             for(int j=0;j<=cnt;j++)
38                 if(dp[i][j])
39                 {
40                     add = dp[i][j];
41                      //传入的state是必须要放置的位置,根据dfs来找到那些包含state状态的且能放一个横木板的状态
42                     dfs(1 , ~j&cnt , i);//这里本来取~j就够了,但是有64位,那么最前面为0 的位置取反后也为1,那么~j得到的其实是个负数,所以还要进行&操作
43
44                 }
45         }
46         printf("%I64d\n",dp[h][cnt]);
47     }
48     return 0;
49 }
时间: 2024-07-31 14:33:02

POJ 2411 状压dp的相关文章

POJ 2411 状压DP经典

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 16771   Accepted: 9683 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

POJ 1038 状压DP

一个公司生产一种2*3规模的芯片,但是原材料上面有一些地方是不能用来当作芯片材料的,给出原料大小,及上面不能做原料的点,问你怎么分解,可以使生成芯片最大化. 对M进行三进制状压 last数组存储第i-1行和i-2行状态,cur数组存储i行和i-1行状态 cur[k]=2; // 本行k位置和上行k位置都不可用 cur[k]=1; // 本行k位置可用,上行k位置不可用 cur[k]=0; // 本行和上行位置k均可用 必须用滚动数组,否则爆内存 #include "stdio.h" #

POJ 3254 (状压DP) Corn Fields

基础的状压DP,因为是将状态压缩到一个整数中,所以会涉及到很多比较巧妙的位运算. 我们可以先把输入中每行的01压缩成一个整数. 判断一个状态是否有相邻1: 如果 x & (x << 1) 非0,说明有相邻的两个1 判断一个状态能否放在该行: 如果 (a[i] & state) != state,说明是不能放置的.因为a[i]中存在某个0和对应state中的1,与运算之后改变了state的值 判断相邻两行的状态是否有同一列相邻的1: 如果(state & _state)不

Best Sequence(poj 1699) 状压dp(TSP)

类似于前两天做的那个wordstack.状压的其实有时候爆搜+记忆化也差不多. 就是这个是要与之前的都重合,移位预处理要注意. 理解好第一个样例就行 /* *********************************************** Author :bingone Created Time :2014/12/9 22:48:56 File Name :a.cpp ************************************************ */ #inclu

POJ 3254 状压DP

题目大意: 一个农民有一片n行m列 的农场   n和m 范围[1,12]  对于每一块土地 ,1代表可以种地,0代表不能种. 因为农夫要种草喂牛,牛吃草不能挨着,所以农夫种菜的每一块都不能有公共边. 告诉你 n ,m 和那些地方能种菜哪些地方不能种菜,求农夫一共有多少种方案种菜 解法: 基本思想是状压 也就是用一个int 型的数代表一行的种菜策略,二进制的0代表该位不能种菜,1位代表能种菜,使用位运算使处理速度变快 对于单行行,最多有2^12 种情况,并且 2^12种情况里面还有很多不满足题意的

POJ 2686(状压DP

第一次做状压感觉那一长串for显示了这是个多么暴力的算法呢...1A了倒是挺顺的 #include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<utility> #include<vector> #include<cstring> #include<cmath> #define INF 0x3fffffff #d

poj 3254 状压dp入门题

1.poj 3254  Corn Fields    状态压缩dp入门题 2.总结:二进制实在巧妙,以前从来没想过可以这样用. 题意:n行m列,1表示肥沃,0表示贫瘠,把牛放在肥沃处,要求所有牛不能相邻,求有多少种放法. #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #d

poj 2923 状压dp+01背包

好牛b的思路 题意:一系列物品,用二辆车运送,求运送完所需的最小次数,两辆车必须一起走 解法为状态压缩DP+背包,本题的解题思路是先枚举选择若干个时的状态,总状态量为1<<n,判断这些状态集合里的那些物品能否一次就运走,如果能运走,那就把这个状态看成一个物品.预处理完能从枚举中找到tot个物品,再用这tol个物品中没有交集(也就是两个状态不能同时含有一个物品)的物品进行01背包,每个物品的体积是state[i],价值是1,求包含n个物品的最少价值也就是dp[(1<<n)-1](dp

POJ 1185 状压DP

legal[] 保存所有在当前行可显示的状态,由dfs得到,len[]保存legal[]对应下标状态中的 1 的个数 , 也就是放置炮台的个数 state[i] 表示第 i 行这块区域的土地情况,H表示 1 ,P表示 0 那么每次加入一个legal状态  都要符合 !(legal[i] & state[k]) 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namesp