POJ 1038 Bugs Integrated, Inc. (状态dp)

确实是好题。。。看解题报告看了一天半,才大概明白是怎么样一个过程。最让我纠结的是M,N的行列问题。。。。文字描述里的M,N和图片上的M,N不对应。本菜只能无比蛋疼而又晕晕乎乎的看。。。

 

 

 

-------------------------------以下部分转自大牛chinaeli-----------------------------------------http://hi.baidu.com/chinaeli/blog/item/b932d4b430d60ac636d3ca34.html

好了,废话说道这里,我来说说我的想法(其实这个方法都是大家用的,我也是效仿别人的方法)
首先说一下如何用三进制表示状态:


假设现在考虑(m,i),从而推出(m,i+1)的状态,因此我们需要记录第i列以及第i-1列的状态,用三进制表示的方法就是A(用0表示),B(用1表示),C(用2表示),其中虚线部分为第i+1列(这个图片旋转90度比较好,做图片时粗心了,嘻嘻),C状态中,因为假如第i列不能放,那么自然第i-1列放和不放都无关紧要了,所以用灰色表示。用p(m)表示第m行,第i列和第i-1列的状态,那么整个的放置状态就是p(0)*3^0+p(1)*3^1......p(M-1)*3^(M-1),转成10进制存储。
状态表示完之后,就要进行状态转移了。


M位的三进制共有3^M种状态,( 0<M<=10),因此最大有59049种状态,采用滚动数组,只要开一个2*59049大小的二维数组就可以了。将边界条件f[0][3^M-1]=0,其余初始为-1,表示该状态不可到达或者尚未到达。然后从第0行开始考虑,枚举每行的状态( 从0到3^M-1 ) ,用s表示,将10进制数转换成3进制存在一个p数组中,接着需要产生一个新的状态,也就是第i+1列的状态:

由于下移了一列,第i-1列我们暂不考虑,之前的A状态变成了A',B变成了B',C变成了C’,之前的0和1都变成新状态的0,原来的2状态变成了新状态的1,但是需要考虑的是第i+1列中是否会存在不能放的区域,假如上图的某个问号中某处不能放置东西,那么不论这个状态是0,还是1,都将变成2,将新状态存于q数组中。现在新生成的状态t是在第i+1行不放置任何东西的状态,那么自然f[i+1][t]=f[i][s].

现在就要进入关键的部分了,用dfs搜索各种摆放的状态。

假设现在枚举到(m,n),目前状态是s,目前已经摆放了cnt个芯片。


如果m<M-2,说明还有至少3列的空处,可以摆放A方式的芯片,但是必须满足(m,n),(m,n-1),(m+1,n),(m+1,n-1),(m+2,n),(m+2,n-1)处空白,也就是q[m]==0 && q[m+1]==0&& q[m+2]==0.那么将状态修改为s=s+2*3^m+2*3^(m+1)+2*3^(m+2)。同理进行B方式的摆放,不过在摆放B时,需要考虑到第n-2行,所以要保证p[m]==0,p[m+1]==0。
当然这一格也可以不摆放。

 

 

 

 

View Code
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define Max(a,b) (a>b?a:b)
#define N 59050
//三进制
int tri[12]={0,1,3,9,27,81,243,729,2187,6561,19683,59049};
int map[151][11];
int dp[2][N];
//前一状态,当前状态
int info_pre[11],info_cur[11];
int n,m,pass;
//初始化
void Init()
{
    memset(map,0,sizeof(map));
    memset(dp,-1,sizeof(dp));
}
//转化 十进制
int switch_ten(int *p)
{
    int i,ans;
    for(i=1,ans=0;i<=m;i++){
        ans += p[i]*tri[i];
    }
    return ans;
}
//转化 三进制
void switch_tri(int t,int *p)
{
    int i;
    for(i=1;i<=m;i++){
        p[i] = t%3;
        t/=3;
    }
    return ;
}
void dfs(int i,int j,int cnt,int stata)
{
    int k;
    dp[i%2][stata] = Max(dp[i%2][stata],cnt);
    if(j >= m ) return ;
    if(!info_pre[j] && !info_pre[j+1] && !info_cur[j] && !info_cur[j+1]){
        // 3*2 排列时 
        info_cur[j] = info_cur[j+1] = 2;
        k = switch_ten(info_cur);
        dfs(i,j+2,cnt+1,k);
        info_cur[j]=info_cur[j+1]=0;
    }
    if(j < m-1 && !info_cur[j] && !info_cur[j+1] && !info_cur[j+2]){
        // 2*3 排列时
        info_cur[j] = info_cur[j+1] = info_cur[j+2] = 2;
        k = switch_ten(info_cur);
        dfs(i,j+3,cnt+1,k);
        info_cur[j] = info_cur[j+1] = info_cur[j+2] = 0;
    }
    dfs(i,j+1,cnt,stata);
    return ;
}
int main()
{

    int t,k,i,j,l,stata,ans;
    scanf("%d",&t);
    while(t--){    
        Init();
        scanf("%d%d%d",&n,&m,&k);
        while(k--){
            scanf("%d%d",&i,&j);
            map[i][j] = 1;
        }
        for(i=1;i<=m;i++){
            info_pre[i] = map[1][i] + 1;
        }
        stata = switch_ten(info_pre);
        dp[1][stata] = 0;
        for(i=2;i<=n;i++){

            for(j=0;j<tri[m+1];j++)dp[i%2][j] = -1;
        
            for(j=0;j<tri[m+1];j++){
                
                if(dp[(i+1)%2][j] == -1)continue;
            
                switch_tri(j,info_pre);
            
                for(l=1;l<=m;l++){
                    if(map[i][l])
                        info_cur[l] = 2;
                    else info_cur[l] = Max(info_pre[l]-1,0);
                }
                pass = dp[(i+1)%2][j];
                dfs(i,1,pass,switch_ten(info_cur));
            }
        }
        for(i=0,ans=0;i<tri[m+1];i++){
            ans = Max(ans,dp[n%2][i]);
        }
        printf("%d\n",ans);
    
    }
    return 0;
}

 

 

 

 

posted @ 2012-07-30 15:20  AC_Von  阅读(1358)  评论(0编辑  收藏  举报