轮廓线DP(插头DP 裸 经典骨牌)

来源:互联网 发布:linux解压zip 编辑:程序博客网 时间:2024/06/02 10:06

引言:所谓轮廓线,不是某一行,或者某一列,而是指某一个特定轮廓的状态。


放置骨牌的约定:(保证放置有最优子结构)
假设我们正在放置第i行的骨牌,那么会有下面3种方式:

灰色表示已经有的骨牌,绿色表示新放置的骨牌。
每一种放置方法解释如下,假设当第i行的状态为x,第i-1行的状态为y:

  • 第i行不放置,则前一行必须有放置的骨牌。x对应二进制位为0,y对应二进制位为1。
  • 第i行竖放骨牌,则前一行必须为空。x对应二进制位为1,y对应二进制位为0。
  • 第i行横向骨牌,则前一行必须两个位置均有骨牌,否则会产生空位。x对应二进制位为1,y对应二进制位为1。



    简单的例子:

    Tiling a Grid With Dominoes

    http://acm.hdu.edu.cn/showproblem.php?pid=1992

    限制了棋盘宽度为4,数据不超过int,只有长度22以内满足答案。

    手写状态找规律。

    http://www.cnblogs.com/lzsz1212/archive/2012/05/02/2478839.html


    把这个变成更一般的问题,如果不限制棋盘的宽,


    hihocoder 骨牌问题讨论了窄棋盘情况下(2^min(n,m) 小于200),构造转移矩阵,快速幂的求法。

    http://hihocoder.com/contest/hiho43/problem/1

    转移矩阵构造法,y为i-1行状态,x为i行状态,dfs构造是K^2的复杂度,K=min(n,m)。

    void dfs(int x,int y,int col){    if(col==K){        d[y][x]=1;        return;    }    dfs(x<<1,(y<<1)|1,col+1);    dfs((x<<1)|1,y<<1,col+1);    if(col+2<=K){        dfs((x<<2)|3, (y<<2)|3, col+2);    }}

    那么复杂度就是k^3*logn,当k<=7时(2^7 = 128)的计算效率是可以接受的。

    #include <cstdio>#include <iostream>using namespace std;typedef long long LL;const int maxn = 1<<7;const int mod = 12357;int d[maxn][maxn];int K,ALL;void dfs(int x,int y,int col){    if(col==K){        d[y][x]=1;        return;    }    dfs(x<<1,(y<<1)|1,col+1);    dfs((x<<1)|1,y<<1,col+1);    if(col+2<=K){        dfs((x<<2)|3, (y<<2)|3, col+2);    }}void mul(int a[][maxn],int b[][maxn],int c[][maxn]){    for(int i=0;i<ALL;i++){        for(int j=0;j<ALL;j++){            c[i][j]=0;        }    }    LL t;    for(int i=0;i<ALL;i++){        for(int j=0;j<ALL;j++){            if(!a[i][j])continue;            for(int k=0;k<ALL;k++){                if(b[j][k]){                    t=(LL)a[i][j]*b[j][k];                    t+=c[i][k];                    c[i][k]=t%mod;                }            }        }    }}void cpy(int a[][maxn],int b[][maxn]){    for(int i=0;i<ALL;i++){        for(int j=0;j<ALL;j++){            a[i][j]=b[i][j];        }    }}void E(int a[][maxn]){    for(int i=0;i<ALL;i++){        a[i][i]=1;        for(int j=i+1;j<ALL;j++){            a[i][j]=a[j][i]=0;        }    }}int e[maxn][maxn],tmp[maxn][maxn];int main(){//    freopen("data.in","r",stdin);    int n;    scanf("%d%d",&K,&n);    dfs(0,0,0);    ALL=1<<K;    E(e);    while(n>0){        if(n&1) {            mul(e,d,tmp);            cpy(e,tmp);        }        mul(d,d,tmp);        cpy(d,tmp);        n>>=1;    }    printf("%d\n",e[ALL-1][ALL-1]);    return 0;}



    uva11270同此题 ,大白书精讲,但是n*m<101,棋盘可能不是窄棋盘,k^3*logn矩阵运算会超时。

    考虑到n<=10,可以构造2^n*n个转移方程,m轮递求解。

    bfs可以避免访问不能求解的状态。状态st=k4k3k2k1k0,ki表示一个二进制位,有方块就为1,否则为0。


    #include <cstdio>#include <iostream>#include <cstring>using namespace std;typedef long long LL;typedef LL type;typedef pair<int,LL> pil;#define mp make_pair#define FF first#define SS secondconst int maxn = 1<<10;int p[2][maxn];pil q[2][maxn];int tail[2];void init(int cur){    memset(p[cur],-1,sizeof p[cur]);    tail[cur]=0;}int main(){    int n,m,cur,st,pos,nst,hi;    LL cnt;    while(scanf("%d%d",&m,&n)!=EOF){        if(m>n) swap(m,n);        cur=0;        st = (1<<m)-1;        hi = 1<<(m-1);        init(cur);        p[0][st]=tail[cur];        pos = tail[cur]++;        cnt = 1;        q[cur][pos]=mp(st,cnt);        for(int i=0;i<n;i++){            for(int j=0;j<m;j++,cur^=1){                init(cur^1);                for(int k=tail[cur]-1;k>=0;k--){                    st = q[cur][k].FF;                    cnt = q[cur][k].SS;                    if(st & hi){                        nst = (st^hi)<<1;                        if((pos=p[cur^1][nst])==-1){                            pos = tail[cur^1]++;                            p[cur^1][nst] = pos;                            q[cur^1][pos]=mp(nst,cnt);                        }else{                            q[cur^1][pos].SS += cnt;                        }                        if(j && !(st&1)){                            nst = ((st^hi)<<1)|3;                            if((pos=p[cur^1][nst])==-1){                                pos = tail[cur^1]++;                                p[cur^1][nst] = pos;                                q[cur^1][pos]=mp(nst,cnt);                            }else{                                q[cur^1][pos].SS += cnt;                            }                        }                    }else{                        nst = (st<<1)|1;                        if((pos=p[cur^1][nst])==-1){                            pos = tail[cur^1]++;                            p[cur^1][nst] = pos;                            q[cur^1][pos]=mp(nst,cnt);                        }else{                            q[cur^1][pos].SS += cnt;                        }                    }                }            }        }        st = (1<<m)-1;        pos = p[cur][st];        cnt = q[cur][pos].SS;        printf("%lld\n",cnt);    }    return 0;}

    对于更复杂的插头DP问题后面再讨论


  • 1 0
    原创粉丝点击