poj 1038 Bugs Integrated, Inc. 三进制状态压缩 DFS 滚动数组

  把底边贴着第 x 行底边放置的芯片 称为第 x 行芯片。

  由于芯片长度只有 3, 所以第 x 行芯片的放置只受 x-1行 和 x-2 行放置情况的影响。

  同时由于一旦 方格 (x-1, y)被黑色记号或其他芯片 占据,则方格(x-2,y)即便空闲对第 x行芯片的放置也毫无意义。

  所以,只需记录的状态只有:

    一, a[x] = 0  表示方格(x-1,y)空闲,方格(x-2,y)空闲

    二, a[x] = 1  表示方格(x-1,y)空闲,方格(x-2,y)被占据

    三, a[x] = 3, 表示方格(x-1,y)被占据

  意味着对于 I 层, 其状态 J, 保存着  I,I-1 层 芯片的放置方法,

  所以我们通过 dp(I,J)更新 I+1 层时,同样要更新 I+1 的状态信息 K, 其保存着 I+1, I层信息

  

  状态 dp(i,j)表示 前 i 层 状态为 J 的最大芯片数量

    情况一, 当前列y 不放,从 y+1 列开始考虑

    情况二, 当前两列 ( y, y+1 )  合法,放 3*2 芯片, 更新 I+1层状态 , 再去考虑 y+2列

    情况三, 当前三列(y,y+1,y+2)合法,放 2*3 芯片, 更新 I+1 层状态,再去考虑 y+3列

  这里因为  3^10 接近 60000,  又行数 n = 150,  因为只需要保存两层间关系,所以可以通过滚动数组来 优化空间。

  另,处理列的组合问题需要 递归DFS,通过回溯来解决。 另外,对于黑点,我们其实可以合并到 芯片占据的类型中去,

而不需要单独处理,这样就能同过 I层的放置序列 P(p1,p2,...,pm),I+1层的放置序列Q (Q1,Q2,。。,Qm),来决策了。

 

解题代码

View Code
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>

const int N = 60000;
int vis[155][11];
int dp[2][60000], A[11], P[11], Q[11];
int n, m, K, mask;

inline int MAX(int a,int b)
{
    return a>b?a:b;
}
int GetState( int f[] )
{
    int res = 0;
    for(int i = 1; i <= m; i++)
        res += (f[i]*A[i-1]);
    return res;
}
void GetBack( int x, int f[] )
{
    for(int i = 1; i <= m; i++)
    {
        f[i] = x%3;    x /= 3;
    }
}

void dfs( int i, int x, int num) // 第i行,x列, 数量为num
{
    if( x > m ) return;    
    int cur = (i+1)&1, nxt = i&1, k = GetState(Q) ;    
        
    // 当前层可不放任意芯片    
    dp[nxt][k] = MAX( dp[nxt][k], dp[cur][ GetState(P) ] );
    
    //情况(1) 当方格 (i+1,x) 不放置芯片时,那么考虑方格(i+1,x), 且 x < m 

    //情况(2) 如果 P[x],P[x+1] = 0, 且区域 (i-1,i+1), (x,x+1)均无黑点 且 i >= 2 && x < m
    //则 竖立放置芯片 3*2, 左下角坐标为 (i+1,x)
    if( (x<=(m-1)) && ( (P[x]==0) && (P[x+1]==0) ) && ( (Q[x]==0)&&(Q[x+1]==0))  )
    {
        Q[x] = Q[x+1] = 2;    
        int kk = GetState(Q);
        dp[nxt][kk] = MAX( dp[nxt][kk], num+1 );
        dfs( i, x+2, num+1 );    
        Q[x] = Q[x+1] = 0;    
    }

    //情况(3) 如果 P[x],P[x+1],P[x+2] <= 1, 且区域 (i,i+1),(x,x+2) 均无黑点 且 i >= 1 && x < m-1
    //则 横着放置芯片 2*3, 左下角坐标为 (i+1,x)
    if( (x<=(m-2)) && ( (!Q[x])&&(!Q[x+1])&&(!Q[x+2]) )  )
    {        
        Q[x]=Q[x+1]=Q[x+2] = 2;    
        int kk = GetState(Q);
        dp[nxt][kk] = MAX( dp[nxt][kk], num+1 );
        dfs( i, x+3, num+1);
        Q[x]=Q[x+1]=Q[x+2] = 0;    
    }
    
    dfs( i, x+1, num );  
}
void solve()
{
    // 初始化为 -1,标明该 状态不可用
    memset( dp, 0xff, sizeof(dp) );
    memset( P, 0, sizeof(P));
    memset( Q, 0, sizeof(Q));
    for(int i = 1; i <= m; i++)
        P[i] = vis[1][i]+1;  //损坏点看作 已占用。。
    // 初始化第一层状态, 抽象把第0层看作皆被占用,合法状态仅一种,且方案数为0
    dp[1][ GetState(P) ] = 0;     

    mask = A[m];
    // 0: i,i-1层位置皆为空
    // 1:i层为空,i-1层已占用
    // 2:i层已占用
    for(int i = 2; i <= n; i++)
    {
        int cur = (i+1)&1, nxt= i&1;    
        // 已知 dp(i,j) 状态j表示 i,i-1层 放置编号
        // 初始化下一层nxt,初始时皆为不可用
        memset( dp[nxt], 0xff, sizeof(dp[nxt]) );

        for(int j = 0; j < mask; j++)
        {
            if( dp[cur][j] == -1 ) continue; //若当前状态不合法    
            //I层 放置编号J 解压成 放置序列 P[]        
            GetBack( j, P );
            
            //获取 I+1层 放置序列 Q[], 且把第I+1层 损坏位置看做已占用,添加到状态中
            for(int x = 1; x <= m; x++)
            {
                if( vis[i][x] ) Q[x] = 2;
                else
                { //去除掉 I-1层 的信息,存储I,I+1放置信息
                    Q[x] = P[x]-1;
                    if( Q[x] < 0 ) Q[x] = 0; 
                }
            }
            // 获取第I+1行 放置状态以及数量    
            dfs( i, 1,  dp[cur][j] ); //行i,列j,目前芯片数量 dp[cur][j]
        }
    }

    int ans = 0;
    for(int i = 0; i < mask; i++)
        ans = MAX( ans, MAX(dp[0][i],dp[1][i]) );
    printf("%d\n", ans );
}
int main()
{
    freopen("test.in","r",stdin);
    freopen("mytest.out", "w", stdout);
    A[0] = 1;
    for(int i = 1; i <= 10; i++ ) A[i] = 3*A[i-1];
    int T;
    scanf("%d", &T);
    while( T-- )
    {
        scanf("%d%d%d", &n,&m,&K);    
        int x, y; 
        memset( vis, 0, sizeof(vis));
        
        for(int i = 0; i < K; i++)
        {
            scanf("%d%d", &x,&y);
            vis[x][y] = 1;
        }
    
        solve();
    }
    return 0;
}

 

  

posted @ 2013-01-15 20:17  yefeng1627  阅读(1918)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor