BZOJ 2331 SCOI 2011 地板 插头DP

来源:互联网 发布:游戏推广系统源码 编辑:程序博客网 时间:2024/04/29 08:55

求用L形方块占满有障碍棋盘的方案数。

对于每个插头,定义状态有:

  • 0 - 无插头
  • 1 - 入插头:指向L的拐点的称为入。
  • 2 - 出插头:相对地。

那么,就可以分情况讨论了。下面以左、上以及下、右的顺序描述插头。

  • 均无插头
    • 无和入或入和出(即L的一个端点)
    • 均为出(即L的拐点)
  • 均有插头
    • 均为出(即L的拐点,合并两插头)
    • 其余情况均不合法。
  • 只有一个有插头
    • 左为出:延续(下无右出)或停止(下右无)
    • 左为入:延续(下无右入)或拐弯(下出右无)
    • 上的情况类似。

注意到每个状态都可能扩展出很多状态,实际状态数会爆炸。
发现如果上层跑完一次的轮廓线,最右边不为无插头的状态显然是不合法的,剔除,然后就神奇地A了。。。
不过讲道理拿到以前的题目一点变化都没有。。。。

#include <cstdio>#include <algorithm>#include <cstring>#include <map>#define rep(i,j,k) for(i=j;i<k;i++)using namespace std;const int mod = 20110520;const int state[] = {0, -1, 1, 0};int get_bit(int sta, int i) { return (sta >> (i << 1)) & 3; }int set_bit(int &sta, int i, int x) {    sta = (sta & ~(3 << (i << 1))) | (x << (i << 1));}const int HASH_STORAGE = 2000000, HASH_SIZE = 100007;struct HashMap {    int key[HASH_STORAGE], val[HASH_STORAGE];    int head[HASH_SIZE], next[HASH_STORAGE], sz;    void clear() { sz = 0; memset(head, -1, sizeof head); }    void add(int k, int v) {        int t = k % HASH_SIZE;        for (int i = head[t]; i != -1; i = next[i])            if (key[i] == k) {                (val[i] += v) %= mod;                return;            }        key[sz] = k; val[sz] = v; next[sz] = head[t]; head[t] = sz++;    }} h[2], *cur, *last;char mp[101][101], MP[101][101];int bx, by, n, m;void update(int x, int y, int sta, int v) {    int l = get_bit(sta, y), s;    int t = get_bit(sta, y + 1);    #define create(i,j) s=sta,set_bit(s,y,i),set_bit(s,y+1,j),cur->add(s,v)    if (mp[x][y] == '*') {        if (!l && !t) create(0, 0);        return;    }    if (!l && !t) {        create(0, 1); create(1, 0); create(2, 2);    } else if (!l || !t) {        if (l == 2) create(0, 0), create(0, 2);        else if (l == 1) create(0, 1), create(2, 0);        if (t == 2) create(0, 0), create(2, 0);        else if (t == 1) create(1, 0), create(0, 2);    } else if (l == 1 && t == 1) create(0, 0);}int solve() {    int i, j, k;    cur = h; last = h + 1;    last->clear(); last->add(0,1);    rep(i,0,n) {        int rate = 2;        rep(j,0,m) {            cur->clear();            rep(k,0,last->sz) {                if (rate == 2 && get_bit(last->key[k], m)) continue;                update(i, j, last->key[k] << rate, last->val[k]);            }            rate = 0;            swap(cur, last);        }    }    int ans = 0;    rep(k,0,last->sz) if (last->key[k] == 0) {        ans = last->val[k]; break;    }    return ans;}int main() {    int i, j;    scanf("%d%d", &n, &m);    rep(i,0,n) scanf("%s", mp[i]);    if (m > n) {        rep(j,0,m) rep(i,0,n) MP[j][i] = mp[i][j];        swap(n, m);        rep(i,0,n) rep(j,0,m) mp[i][j] = MP[i][j];    }    bx = by = -1;    for(i=n-1;i>=0;--i) for(j=m-1;j>=0;--j)        if (mp[i][j] == '_') bx = i, by = j, i = -1, j = -1;    printf("%d", solve());    return 0;}

2331: [SCOI2011]地板

Time Limit: 5 Sec Memory Limit: 128 MB
Submit: 827 Solved: 350
[Submit][Status][Discuss]

Description

lxhgww的小名叫“小L”,这是因为他总是很喜欢L型的东西。小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板。现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案?
需要注意的是,如下图所示,L型地板的两端长度可以任意变化,但不能长度为0。铺设完成后,客厅里面所有没有柱子的地方都必须铺上地板,但同一个地方不能被铺多次。

Input

输入的第一行包含两个整数,R和C,表示客厅的大小。
接着是R行,每行C个字符。’_’表示对应的位置是空的,必须铺地板;’*’表示对应的位置有柱子,不能铺地板。

Output

输出一行,包含一个整数,表示铺满整个客厅的方案数。由于这个数可能很大,只需输出它除以20110520的余数。

Sample Input

2 2*___

Sample Output

1

HINT

RC<=100

Source

Day1

0 0
原创粉丝点击