IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    POJ-1753 解题报告

    林 达意发表于 2012-06-01 12:07:03
    love 0

    题意简述

    4*4的格子放有黑白两面的棋子16个,翻转任一棋子会使其上下左右棋子一同翻转。给定初始状态,求达到所有棋子全黑或全白的最少翻转次数。如果无解,输出Impossible。

    POJ1753附图

    POJ1753附图

    算法分析

    首先容易证明:

    1、翻转棋盘上任意个棋子,棋盘达到的状态与各棋子被翻转的先后顺序无关。

    2、对每个棋子进行2次及以上的翻转没有意义。∵ 偶数次翻转结果与不翻转相同,奇数次翻转结果与翻转1次相同。

    于是问题被简化为:棋盘上部分棋子经过1次翻转,能使棋盘达到全黑或全白状态。求所需翻转棋子的最少数目。

    因为一共只有16个棋子,故只有2^16种可能。

    ——————————————————————分割线——————————————————————

    具体算法:

    DFS。

    输入时将数据转化为1维01数组。

    dfs(array,position,step,answer)四个形参,array传递数组,position传递当前位置,step传递深度(也即翻转个数),ans传递当前最优答案。

    深搜时先判断当前状态是否满足条件,满足则和当前答案对比,若更优则替换。返回。

    若深度超出16也返回。

    改变当前位置,以及上下左右共5个点状态。

    dfs(array,position+1,step+1,answer)。

    回溯。只需再次改变5个点状态即可。

    dfs(array,position+1,step,answer)。

    Problem Status: AC。时间94ms,内存360k

    ——————————————————————分割线——————————————————————

    TIPS:

    WA了好几次。有几个小细节要注意:

    1、翻转时,如果遇到行末则不翻转下一个,遇到行首则不翻转上一个。可以通过mod 4的值来判断。

    2、DFS时,需先判断是否满足条件(全黑或全白)再判断位置是否越界(position<16)。因为有可能所有棋子都需要翻转,由于判断本次翻转是否成功是在下一层递归中,所以即使position越界也需先验证一次上次翻转后是否达到所需状态。

    ——————————————————————分割线——————————————————————

    优化:

    1、一维01数组可以使用一个int类型变量替代。2个字节16位就可以表示了。翻转操作采用位运算实现。(这个还没试过,据说时间达到16ms,以后有机会试下。)

    2、网上的解题报告说据说有一种枚举可以达到时间0ms?还没找到具体算法。要去膜拜下。

    程序样例

    #include
    
    void change(int s[],int pos)
    {
        if (s[pos]==0) s[pos]=1; else s[pos]=0;
        if ((pos+1<16)&&(pos%4!=3))
            if (s[pos+1]==0) s[pos+1]=1; else s[pos+1]=0;
        if ((pos-1>=0)&&(pos%4!=0))
            if (s[pos-1]==0) s[pos-1]=1; else s[pos-1]=0;
        if (pos+4<16)
            if (s[pos+4]==0) s[pos+4]=1; else s[pos+4]=0;
        if (pos-4>=0)
            if (s[pos-4]==0) s[pos-4]=1; else s[pos-4]=0;
    }
    
    int finish(int s[])
    {
        int i;
        for(i=1;i<16;i++)
            if (s[i]!=s[0]) return 0;
        return 1;
    }
    
    void try(int s[], int pos, int step, int *ans)
    {
        if (finish(s))
        {
            if (step<*ans) *ans=step;
            return;
        }
        if (pos>15) return;
        change(s,pos);
        try(s,pos+1,step+1,ans);
        change(s,pos);
        try(s,pos+1,step,ans);
    }
    
    int main()
    {
        int s[16];
        int i,j,ans=17;
        char t[5];
        for(i=0;i<4;i++)
        {
            scanf("%s",t);
            for(j=0;j<4;j++)
                if (t[j]=='b') s[i*4+j]=1;
                else s[i*4+j]=0;
        }
        try(s,0,0,&ans;);
        if (ans==17) printf("Impossible"); else printf("%d",ans);
        system("pause");
        return 0;
    }
    


沪ICP备19023445号-2号
友情链接