☆【动态规划】【SCOI2011】地板
来源:互联网 发布:炉石dk算法术还是随从 编辑:程序博客网 时间:2024/04/28 20:54
题目描述】lxhgww的小名叫“小L”,这是因为他总是很喜欢L型的东西。小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板。现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案?需要注意的是,如下图所示,L型地板的两端长度可以任意变化,但不能长度为0。铺设完成后,客厅里面所有没有柱子的地方都必须铺上地板,但同一个地方不能被铺多次。
【输入】输入的第一行包含两个整数,R和C,表示客厅的大小。接着是R行,每行C个字符。’_’表示对应的位置是空的,必须铺地板;’*’表示对应的位置有柱子,不能铺地板。【输出】输出一行,包含一个整数,表示铺满整个客厅的方案数。由于这个数可能很大,只需输出它除以20110520的余数。【样例输入1】2 2*___【样例输出1】1【样例输入2】3 3____*____ 【样例输出2】8【数据范围】测试点编号 数据范围1, 2 R*C <= 253, 4, 5 R*C<=100并且(R = 2或者C = 2)6, 7, 8, 9, 10 R*C <= 100这是一道基于连通性的状态压缩动态规划。
状态定义如下:设每个L型地砖都有两个不同伸展方向,并规定从转角处两端为向外,从两端到转角处为向内。使用0,1,3三种标记表示三种不同的插头,0表示没有插头,1表示向内的插头,3表示向外的插头。逐格递推,每次用一个四进制数来表示这一行所有插头的状态,按从左到右的顺序,左边为高位,右边为低位。
插图如下:
状态转移的时候,要用一个开散列记录状态,避免多次枚举。
Accode:
#include <cstdio>#include <cstring>#include <cstdlib>#include <bitset>const char fi[] = "floor.in";const char fo[] = "floor.out";const int maxR = 110;const int maxN = 1 << 18;const int MOD = 20110520;const int HMOD = 262143;struct Node{ int S, ID; Node *next;};std::bitset <maxR> mp[maxR];Node *Hash[HMOD + 1];Node tmp[maxN];int cnt[2];int f[2][maxN];int state[2][maxN];//以上三个数组使用零壹滚动。int n, m, pst, ths;void init_file(){freopen(fi, "r", stdin);freopen(fo, "w", stdout);return;}void readdata(){ scanf("%d%d", &n, &m); for (int i = 1; i < n + 1; ++i) { getchar(); for (int j = 1; j < m + 1; ++j) if (getchar() == '_') { if (m > n) mp[j].set(i); else mp[i].set(j); } } if (m > n) std::swap(m, n);return;}inline void recover(){ cnt[ths] = 0; memset(Hash, 0, sizeof(Hash)); return;} //每次需要清空,为下次的状态转移做好空间。inline int get_ID(int S){ unsigned h = S & HMOD; for (Node *p = Hash[h]; p; p = p -> next) if (p -> S == S) return p -> ID;//若该状态已经被生成,则直接返回它的序号。//否则新建一个。 tmp[++cnt[ths]].S = S; tmp[cnt[ths]].ID = cnt[ths]; tmp[cnt[ths]].next = Hash[h]; Hash[h] = &tmp[cnt[ths]]; f[ths][cnt[ths]] = 0; state[ths][cnt[ths]] = S; return cnt[ths];}inline void Add(int &a, int b){ a += b; if (a >= MOD) a -= MOD; return;}void work(){ pst = 0, ths = 1;//零壹滚动中的两指针。 recover(); f[ths][get_ID(0)] = 1;//置初始值为1。 for (int i = 1; i < n + 1; ++i) for (int j = 1; j < m + 1; ++j) { std::swap(pst, ths); recover();//每次要记得清空。 int q = (m - j) << 1;//记录当前需要转移的右插头的位置(相对于最右端)。 int p = q + 2;//记录当前需要转移的下插头的位置(相对于最右端)。 for (int k = 1; k < cnt[pst] + 1; ++k) { int val = f[pst][k];//当前状态的方案数。 int Last = state[pst][k];//当前状态对应的四进制数。 if (j == 1) { if (Last & 3) continue; else Last >>= 2; }//若换行,首先判断末尾是否有多余的插头,//若有,则此状态作废;否则将末尾的插头//移动到下一行的开头,以便下一行的转移。 int wp = (Last >> p) & 3;//当前的右插头。 int wq = (Last >> q) & 3;//当前的下插头。 int Now = Last - (wp << p) - (wq << q);//构造一个新的插头,将转移的目标状态//所对应的两个插头先清空。 if (!mp[i][j]) { if (!wp && !wq) Add(f[ths][get_ID(Now)], val);//若该方块为障碍且无插头则可行。 } else { if (!wp && !wq)//无插头的情况,见图1-0。//此时可以新建插头,分三种情况讨论。 { Add(f[ths][get_ID(Now | (1 << p))], val); //见图1-2。 Add(f[ths][get_ID(Now | (1 << q))], val); //见图1-1。 Add(f[ths][get_ID(Now | (3 << p) | (3 << q))], val); //见图1-3。 } else if (!wp || !wq)//只有一个插头的情况。见图2-0(1),2-0(2),//2-0(3)以及2-0(4),也要分情况讨论。 { Add(f[ths][get_ID(Now | (wp << q) | (wq << p))], val);//延续原来的插头,见图2-1(1)和2-1(2)。 if (wp == 1) //见图2-0(2)。 Add(f[ths][get_ID(Last | (2 << p))], val);//插头可在此转弯,见图2-2。 else if (wq == 1) Add(f[ths][get_ID(Last | (2 << q))], val);//同理如上。 else if (wp == 3 || wq == 3) //如图2-0(4)。 Add(f[ths][get_ID(Now)], val);//插头可在次终止,见图2-3。 } else if (wp == 1 && wq == 1)//两插头相遇,可以构成一个L,见图3-0。 Add(f[ths][get_ID(Now)], val); //见图3-1。 } } } printf("%d\n", f[ths][get_ID(0)]);return;}int main(){init_file();readdata();work();return 0;}第二次做:
#include <cstdio>#include <cstdlib>#include <algorithm>#include <string>const char fi[] = "floor.in";const char fo[] = "floor.out";const int maxN = 110;const int maxSTATUS = 0x100000;const int HMOD = 0x3ffff;const int MOD = 20110520;struct Node {int S, ID; Node *next;};Node tmp[maxSTATUS], *Hash[HMOD + 1];char mp[maxN][maxN];int f[2][maxSTATUS], status[2][maxSTATUS];int cnt[2], n, m, pst, ths = 1;void init_file(){ freopen(fi, "r", stdin); freopen(fo, "w", stdout); return;}void readdata(){ scanf("%d%d", &n, &m); for (int i = 0; i < n; ++i) { scanf("\n"); for (int j = 0; j < m; ++j) { char ch = getchar(); if (m > n) mp[j][i] = ch; else mp[i][j] = ch; } } if (m > n) std::swap(n, m); return;}inline int get_ID(int S){ int h = S & HMOD; for (Node *p = Hash[h]; p; p = p -> next) if (p -> S == S) return p -> ID; int ID = cnt[ths]; tmp[ID].ID = ID; tmp[ID].S = S; tmp[ID].next = Hash[h]; Hash[h] = &tmp[ID]; f[ths][ID] = 0; status[ths][ID] = S; return cnt[ths]++;}inline void Add(int &a, int b){a += b; if (a >= MOD) a -= MOD; return;}void work(){ f[ths][get_ID(0)] = 1; for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j) { std::swap(pst, ths); cnt[ths] = 0; memset(Hash, 0, sizeof Hash); int p = (m - j) << 1, q = p - 2; for (int k = 0; k < cnt[pst]; ++k) { int val = f[pst][k]; int Last = status[pst][k]; if (!j) { if (Last & 3) continue; else Last >>= 2; } int wp = (Last >> p) & 3, wq = (Last >> q) & 3, Now = Last - (wp << p) - (wq << q); if (mp[i][j] == '*') {if (!wp && !wq) Add(f[ths][get_ID(Now)], val);} else if (!wp && !wq) { Add(f[ths][get_ID(Now | (1 << p))], val); Add(f[ths][get_ID(Now | (1 << q))], val); Add(f[ths][get_ID(Now | (3 << p) | (3 << q))], val); } else if (!wp || !wq) { Add(f[ths][get_ID(Now | (wp << q) | (wq << p))], val); if (wp == 1) Add(f[ths][get_ID(Last | (2 << p))], val); else if (wq == 1) Add(f[ths][get_ID(Last | (2 << q))], val); else Add(f[ths][get_ID(Now)], val); } else if (wp == 1 && wq == 1) Add(f[ths][get_ID(Now)], val); } } printf("%d\n", f[ths][get_ID(0)]); return;}int main(){ init_file(); readdata(); work(); return 0;}
- ☆【动态规划】【SCOI2011】地板
- 【动态规划】【SCOI2011】股票交易
- 【BZOJ 2331】 [SCOI2011]地板
- 2331: [SCOI2011]地板
- [bzoj2331][SCOI2011]地板
- 2331: [SCOI2011]地板
- BZOJ 2331 [SCOI2011]地板
- BZOJ2331 [SCOI2011]地板
- BZOJ2331: [SCOI2011]地板
- 【SCOI2011】bzoj2331 地板
- bzoj2331: [SCOI2011]地板
- 【bzoj2331】[SCOI2011]地板 插头dp
- Bzoj2331[SCOI2011]地板:插头dp
- [插头DP] BZOJ2331 && SCOI2011 地板
- BZOJ 2331 SCOI2011 地板 插头DP
- [BZOJ]2331: [SCOI2011]地板 插头DP
- 状态压缩动态规划 POJ 2411 (编程之美-瓷砖覆盖地板)
- 状态压缩与动态规划(DP)---编程之美---瓷砖覆盖地板---POJ2411
- sysprep 可能的错误及解决方法
- strings 提取 features
- 2012年网页设计趋势
- struts2异常处理
- C# 使用SendMessage 函数
- ☆【动态规划】【SCOI2011】地板
- FTP上传文件,Apache篇
- 在C#中SendMessage和PostMessage的参数传递
- 常用的正则表达式
- 用C#调用Windows API和其它进程通信
- Android中的.9.png图形的机制及制作和使用方法
- 手机开发平台指南、教程和资料介绍(修改稿)
- C#遍历进程获取主窗口句柄
- 项目管理的求生指南