BZOJ 2331 [SCOI2011]地板
来源:互联网 发布:网络ssid没有wlan 编辑:程序博客网 时间:2024/04/26 21:37
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2331
题意:给一个n*m的格子,有一些地方可以放置,有一些地方不可以放置,现在只用'L'型的地板铺满整个格子,L型地板的两端长度可以任意变化,但不能长度为0。铺设完成后,可以放置的地方都必须铺上地板,但同一个地方不能被铺多次,求不同的铺法。
思路:插头dp从上到下,左到右逐格递推,所以我们只定义向下和向右两个方向,那么对于任意的L型的格子内部的连通线,在向下和向右的方向上只能是进入到拐点或从拐点内出。我们就用4进制表示轮廓线,0表示此处无连通,1表示(向下/向右方向上)一个入的连通线,2表示(向下/向右方向上)一个出的连通线。因为L型两端的长度至少为1,所以只要保证有一个连通线的长度就可以了,一个连通线跨越两格,除去拐点格,还剩下一格长度。然后就是根据上方和左方的连通性来转移状态。n*m<=100,所以若m>n就翻转地图,让小的成为m。
#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define LL long long#define Clean(x,y) memset(x,y,sizeof(x))int n,m;int pre,cur;char g[100][100];const int Hash = 10007;const int maxn = 2009999;const int mod = 20110520;LL ans;int bit = 3;int inc = 2;int code[30];struct hash_table{ int head[Hash] , next[maxn]; LL state[maxn],value[maxn]; int size; void clear() { size = 0; Clean(head,-1); } void push( LL S , LL V ) { int index = S % Hash; for( int k = head[index]; k != -1; k = next[k] ) if ( state[k] == S ) { value[k] = (value[k] + V) % mod; return; } state[size] = S , value[size] = V; next[size] = head[index] , head[index] = size++; }}dp[2];inline void decode( LL S , int m ){ for( int i = 0; i <= m; i++ ) code[i] = S & bit , S >>= inc;}inline LL encode( int m ){ LL ans = 0; for( int i = m; i >= 0; i-- ) { ans <<= inc; ans |= code[i]; } return ans;}bool check( int m ){ for( int i = 0; i <=m; i++ ) if ( code[i] ) return false; return true;}void DP( int x , int y , int k ){ decode( dp[pre].state[k] , m ); int left = code[y-1] , up = code[y]; LL V = dp[pre].value[k]; code[y] = code[y-1] = 0; if ( g[x][y] == '*' ) //当前格子不可放置 { if ( !left && !up ) dp[cur].push( encode(m) , V ); //左 上 必须无连通才可以转移 } else { if ( !left && !up ) //左、上 无连通 { //当前格子向下延伸成为下方某个拐点的一条边 if ( x < n && g[x+1][y] == '_' ) code[y-1] = 1 , dp[cur].push( encode(m) , V ) , code[y-1] = 0; //当前格子向右延伸成为右方某个拐点的一条边 if ( y < m && g[x][y+1] == '_' ) code[y] = 1 , dp[cur].push( encode(m) , V ) , code[y] = 0; //当前格子可以作为一个拐点格,向下,右延伸。 if ( x < n && y < m && g[x+1][y] == '_' && g[x][y+1] == '_' ) code[y] = 2 , code[y-1] = 2 , dp[cur].push( encode(m) , V ); } else if ( !left || !up ) //有一个连通 { if ( up == 1 )//上方作为入连通 , 也就是说下方应该存在一个拐点 { //继续延伸 if ( x < n && g[x+1][y] == '_' ) code[y-1] = 1 , dp[cur].push( encode(m) , V ) , code[y-1] = 0; //停止延伸 , 此格子作为拐点 , 并且该向右拐出去 if ( y < m && g[x][y+1] == '_' ) code[y] = 2 , dp[cur].push( encode(m) , V ) , code[y] = 0; } else if ( up == 2 ) //作为上方某个拐点的出边 { //继续出 if ( x < n && g[x+1][y] == '_' ) code[y-1] = 2 , dp[cur].push( encode(m) , V ) , code[y-1] = 0; //停止 dp[cur].push( encode(m) , V ); } else if ( left == 1 ) //同上 { if ( x < n && g[x+1][y] == '_' ) code[y-1] = 2 , dp[cur].push( encode(m) , V ) , code[y-1] = 0; if ( y < m && g[x][y+1] == '_' ) code[y] = 1 , dp[cur].push( encode(m) , V ) , code[y] = 0; } else { dp[cur].push( encode(m) , V ); if ( y < m && g[x][y+1] == '_' ) code[y] = 2 , dp[cur].push( encode(m) , V ) , code[y] = 0; } } else //双联通 { if ( left == up && left == 1 ) //必须相等 且都为1,那么此格子可以作为一个拐点 { dp[cur].push( encode(m) , V ); } } }}LL solve(){ cur = 0; dp[0].clear(); dp[0].push( 0 , 1 ); for( int i = 1; i <= n; i++ ) { pre = cur , cur ^= 1; dp[cur].clear(); for( int k = 0; k < ( dp[pre].size ); k++ ) dp[cur].push( dp[pre].state[k]<<inc , dp[pre].value[k] ); for( int j = 1; j <= m; j++ ) { pre = cur , cur ^= 1 , dp[cur].clear(); for( int k = 0; k < dp[pre].size; k++ ) DP( i , j , k ); } } for( int k = 0; k < dp[cur].size; k++ ) if ( dp[cur].state[k] == 0 ) return dp[cur].value[k]; return 0;}void init(){ char s[100]; getchar(); for (int i=1;i<=n;i++){scanf("%s",s+1);for (int j=1;j<=m;j++)if (n<m) g[j][i] = s[j]; else g[i][j] = s[j];}if (n<m) swap(n,m);}int main(){ scanf("%d%d",&n,&m); init(); printf("%lld\n",solve()); return 0;}
0 0
- 【BZOJ 2331】 [SCOI2011]地板
- BZOJ 2331 [SCOI2011]地板
- BZOJ 2331 SCOI2011 地板 插头DP
- [BZOJ]2331: [SCOI2011]地板 插头DP
- 2331: [SCOI2011]地板
- 2331: [SCOI2011]地板
- [bzoj2331][SCOI2011]地板
- BZOJ2331 [SCOI2011]地板
- BZOJ2331: [SCOI2011]地板
- 【SCOI2011】bzoj2331 地板
- bzoj2331: [SCOI2011]地板
- ☆【动态规划】【SCOI2011】地板
- 【bzoj2331】[SCOI2011]地板 插头dp
- Bzoj2331[SCOI2011]地板:插头dp
- [插头DP] BZOJ2331 && SCOI2011 地板
- BZOJ 2331 SCOI 2011 地板 插头DP
- BZOJ 2330: [SCOI2011]糖果
- BZOJ 2330 [SCOI2011]糖果
- 多线程复习-NSOperation
- 红透半边天的VR(虚拟现实)产业
- 【笔记】ngrok安装方法
- Uva1593 Alignment of Code 【vector,stream应用】【习题5-1】
- 并查集(通俗易懂)
- BZOJ 2331 [SCOI2011]地板
- 【水题】hdoj1203 Doubles
- poj 1061 青蛙的约会(扩展gcd)
- 数据结构-串-test.ming
- NYOJ 995 硬币找零(dp)
- 合并两个升序链表(递归)
- 双向循环链表
- django 1.8 出现 TemplateDoesNotExist 错误
- this指针