BOJ 247 Gem And Prince [剪枝爆搜][36th ACM/ICPC Beijing Regional Onsite J]

这题我出的,因为邀请赛时候出了道题意有点问题的模板题,所以这次决定出一道题意清楚的非模板题。
那天躺在床上玩ipad,突然玩到了《终极宝石》,于是乎就动了给这个游戏写外挂的想法,也就变成了一道题,本来是想出给网赛的,但谁知道最后变成了现场赛题目。

judge们看了现场赛最后的题目之后,给这题定为的是第三题,结果由于题目太长+水题太坑+神队带歪了board,这题最后也没几个人过。。。
其实就是一道搜索而已,而且数据很小,时限很松,随手加点剪枝就可以过。

这题明显不存在局部最优解,所以我们想到的解题方法是模拟
+搜索+剪枝。

模拟+搜索:对于每一个局面,用bfs穷举出下一步所有可能存在的消除宝石的方法,每发现这样的一个方法就dfs进入下一层。

(即通过floodfill的方法进行bfs,每得到一个大小大于2的连通块的时候就dfs递归深入下去。这样可以让下面的剪枝得到很好的效果,可以将搜索树的size大大减小。)

剪枝:对于每一个局面,记录它通过之前的步骤已经得到的分数,然后维护它还可能得到的最大的分数(比如已经得到了120分,还有5个红色宝石,4个蓝色宝石,2个黑色宝石,那此局面可能得到的分数一定不超过120+5*5+4*4),通过用这个分数与当前已经得到的最大分数进行比较来进行剪枝。对于大多数数据这个剪枝能剪掉99.9%的节点,所以有时可以跑过10*9的数据,但是这个剪枝也是不稳定的,存在不少数据能把它卡掉。不过对于题目的最大上限8*8,这个剪枝已经足够了。

至于整体的复杂性分析等我有空或者哪位大牛有空可以详细写一下,在这里就不写出了。
我在当judge的时候看到最快的做法是除了以上剪枝之外,还把当前局面hash掉,每次判一下hash,虽然常数会稍微增大,但是重复状态会减少很多。没准加了hash之后可以跑过原来游戏中12*12的解也未可知呢。

话说自己写的标被挂掉了,因为常数太大。。。我在这里就放出我的小队友claire大神的标吧。等我有空也许会去写一份加上神优化hash的标呢。

#include<iostream>
#include
<cstdio>
#include
<cstring>

using namespace std;
const int N=14;
int a[N][N],v[N][N],t[N];
int d[8][2]={-1,-1,-1,0,-1,1,0,-1,0,1,1,-1,1,0,1,1};
int m,n,o,ans;

int  bfs(int p,int q,int a[][N],int id[][N],int w)
{

    
int f[N*N][2];
    
int i,l=1,r=1,x,y;
    f[
1][0]=p; f[1][1]=q;
    v[p][q]
=1;
    
while (l<=r)
    {
        p
=f[l][0]; q=f[l][1];
        id[p][q]
=w;
        
for (i=0; i<8; i++)
         {
             x
=p+d[i][0];
             y
=q+d[i][1];
             
if (x>=1 && x<=&& y>=1 && y<=&& a[x][y]==a[p][q] && v[x][y]==0)
              {
                  r
++; v[x][y]=1;
                  f[r][
0]=x; f[r][1]=y;
              }
         }
        l
++;
    }
    
return r;
}

void change(int b[][N])
{
     
int temp1[N],temp2[N],i,j,x,y,s=0;
     
for (j=1; j<=n; j++)
      {
          x
=0; y=m;
          
for (i=1; i<=m; i++)
           
if (b[i][j]>0) temp1[++x]=i;
          
if (x>0) temp2[++s]=j;
          
while (x>0)
          {
              
if (y!=temp1[x]){ b[y][j]=b[temp1[x]][j]; b[temp1[x]][j]=0;}
              x
--;
              y
--;
          }
      }
     x
=1; y=1;
     
while (x<=s)
     {
         
if (y!=temp2[x]) for (i=1; i<=m; i++) {b[i][y]=b[i][temp2[x]]; b[i][temp2[x]]=0;}
         x
++;
         y
++;
     }
}

void dfs(int s,int a[][N],int sum)
{
    
if (s>ans) ans=s;
    
int c[N*N],e[N*N],id[N][N]={0},w=0;
    memset(v,
0,sizeof(v));
    
for (int i=1; i<=m; i++)
      
for (int j=1; j<=n; j++)
        
if (a[i][j]>0 && t[a[i][j]]>=3 && v[i][j]==0 )
        {
            w
++;
            c[w]
=bfs(i,j,a,id,w);
            e[w]
=a[i][j];
        }
    
int b[N][N];
    
for (int k=1; k<=w; k++)
      
if (c[k]>=3)
      {
              
int sum1=sum-t[e[k]]*t[e[k]];
              
if (t[e[k]]-c[k]>=3)  sum1=sum1+(t[e[k]]-c[k])*(t[e[k]]-c[k]);
              
if (sum1+s+c[k]*c[k]>ans)
              {
                
for (int i=1; i<=m; i++)
                 
for (int j=1; j<=n; j++)
                  
if (id[i][j]==k)
                    b[i][j]
=0else b[i][j]=a[i][j];
                change(b);
                t[e[k]]
-=c[k];
                dfs(s
+c[k]*c[k],b,sum1);
                t[e[k]]
+=c[k];
              }
      }
}

int main()
{
    
int i,j,k;
    
while (scanf("%d%d%d",&m,&n,&o)==3)
    {
        
int sum=0;
        memset(t,
0,sizeof(t));
        
for (i=1; i<=m; i++)
          
for (j=1; j<=n; j++)
           {
               scanf(
"%d",&a[i][j]);
               t[a[i][j]]
++;
           }
        
for (int i=1; i<=o; i++)
          
if (t[i]>=3) sum+=t[i]*t[i];
        ans
=0;
        dfs(
0,a,sum);
        printf(
"%d\n",ans);
    }
}

posted on 2011-10-26 14:06 BUPT-[aswmtjdsj] @ Penalty 阅读(842) 评论(0)  编辑 收藏 引用 所属分类: BOJ Solution Report搜索


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2011年10月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

导航

统计

常用链接

留言簿(1)

随笔分类(150)

随笔档案(71)

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜