首页 > 代码库 > Codevs 2495 水叮当的舞步

Codevs 2495 水叮当的舞步

2495 水叮当的舞步

 

 时间限制: 1 s
 空间限制: 32000 KB
 题目等级 : 钻石 Diamond
 
 
题目描述 Description

  水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变。
  为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~

  地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色。
  水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。
  由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把所有格子的颜色变成一样的。

输入描述 Input Description

  每个测试点包含多组数据。
  每组数据的第一行是一个整数N,表示地摊上的格子有N行N列。
  接下来一个N*N的矩阵,矩阵中的每个数都在0~5之间,描述了每个格子的颜色。
  N=0代表输入的结束。

输出描述 Output Description

  对于每组数据,输出一个整数,表示最少步数。

样例输入 Sample Input

2
0 0 
0 0
3
0 1 2
1 1 2
2 2 1
0

样例输出 Sample Output

0
3

数据范围及提示 Data Size & Hint

  对于30%的数据,N<=5
  对于50%的数据,N<=6
  对于70%的数据,N<=7
  对于100%的数据,N<=8,每个测试点不多于20组数据。

第二组样例解释:
  0 1 2       1 1 2       2 2 2      1 1 1
  1 1 2 --> 1 1 2 --> 2 2 2 --> 1 1 1
  2 2 1       2 2 1       2 2 1      1 1 1

 

来源:Nescafe 21

分类标签 Tags 点此展开 

 
启发式搜索 迭代搜索 搜索

 

感触:(不敢说是思路)

网上说此题最后一个点卡常数,技术分享,dfs肯定跑不快,这不明显坑爹吗!~

网上有大神和我写的差不多的,各种优化,最关键人家是用bfs搜过的。

dfs改bfs,bfs不知道哪写错了。

所有答案都WA了。(bfs部分引去了)

挑了1个多小时,实在调不出来了。放弃了。。。

~~~~(>_<)~~~~ 又舍不得扔。

90分代码:

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;inline const int read(){    register int x=0,f=1;    register char ch=getchar();    while(ch<0||ch>9) ch=getchar();    return ch-0;}const int dx[]={0,0,1,-1};const int dy[]={1,-1,0,0};const int N=9;int n,g[N][N],a[N][N];bool flag,vis[N][N],mark[N]; inline void calc(int &c){    memset(mark,0,sizeof mark);    c=0;    for(int i=1;i<=n;i++){        for(int j=1;j<=n;j++){            if(!mark[a[i][j]]){                mark[a[i][j]]=1;                c++;            }        }    }}void go(int x,int y,int c,int r){    a[x][y]=r;    vis[x][y]=1;    for(int i=0;i<4;i++){        int nx=x+dx[i];        int ny=y+dy[i];        if(!vis[nx][ny]&&nx>0&&nx<=n&&ny>0&&ny<=n&&a[nx][ny]==c){            go(nx,ny,c,r);        }    }}void get_round(int x,int y,int c,int round[]){    vis[x][y]=1;    for(int i=0;i<4;i++){        int nx=x+dx[i];        int ny=y+dy[i];        if(!vis[nx][ny]&&nx>0&&nx<=n&&ny>0&&ny<=n){            if(a[nx][ny]==c) get_round(nx,ny,c,round);            else if(!round[a[nx][ny]]) round[a[nx][ny]]=1;        }    }}/*#define xx first #define yy secondpair<int,int>q[800000];void go(int x,int y,int c,int r){    a[x][y]=r;    vis[x][y]=1;    int h=0,t=1;    while(h!=t){        ++h;        for(int i=0;i<4;i++){            int nx=q[h].xx;            int ny=q[h].yy;            if(!vis[nx][ny]&&nx>0&&nx<=n&&ny>0&&ny<=n&&a[nx][ny]==c){                vis[nx][ny]=1;                a[nx][ny]=r;                ++t;                q[t]=make_pair(nx,ny);            }        }    }}void get_round(int x,int y,int c,int round[]){    vis[x][y]=1;    int h=0,t=1;    while(h!=t){        ++h;        for(int i=0;i<4;i++){            int nx=q[h].xx;            int ny=q[h].yy;            if(!vis[nx][ny]&&nx>0&&nx<=n&&ny>0&&ny<=n){                if(a[nx][ny]==c){                    vis[nx][ny]=1;                    ++t;                    q[t]=make_pair(nx,ny);                }                else if(!round[a[nx][ny]]) round[a[nx][ny]]=1;            }        }    }}*/void dfs(int now,int sum){    if(flag) return ;    int C;    calc(C);    if(now==sum){        if(C==1) flag=1;        return ;    }    if(now+C-1>sum) return ;    memset(vis,0,sizeof vis);    int round[N]={0};    get_round(1,1,a[1][1],round);    for(int i=0;i<=5;i++){        if(i==a[1][1]||!round[i]) continue;        int back[N][N];        memcpy(back,a,sizeof a);        memset(vis,0,sizeof vis);        go(1,1,a[1][1],i);        if(flag) return ;        dfs(now+1,sum);        memcpy(a,back,sizeof back);    }} int main(){    freopen("sh.txt","r",stdin);    for(;;){        n=read();        if(!n) break;        for(int i=1;i<=n;i++){            for(int j=1;j<=n;j++){                g[i][j]=read();            }        }        int ans=0;        for(int k=0;k<=16;k++){            memcpy(a,g,sizeof g);            flag=0;            dfs(0,k);            if(flag){                ans=k;                break;            }        }        printf("%d\n",ans);    }    return 0;}

 

 

 

 

 

 

 

 

 

 

AC的艰辛:

技术分享

题解:

主要是优化了我的“大水漫灌”函数:(基本上都改了)

我们可以发现,每次寻找左上角的格子所在的联通块耗费的时间常数巨大。因此我们在这里寻求突破。

我们引入一个N*N的v数组。左上角的格子所在的联通块里的格子标记为1。左上角联通块周围一圈格子标记为2,其它格子标记为0。如果某次选择了颜色c,我们只需要找出标记为2并且颜色为c的格子,向四周扩展,并相应地修改v标记,就可以不断扩大标记为1的区域,最终如果所有格子标记都是1,那么显然找到了答案。(参考黄学长的博客)

AC代码:

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>const int dx[]={0,0,1,-1};const int dy[]={1,-1,0,0};const int N=9;int n,mp[N][N];int mark[N][N];bool ans,vis[N];int s;inline int get(){    int t=0;    memset(vis,0,sizeof vis);    for(int i=1;i<=n;i++){        for(int j=1;j<=n;j++){            if(!vis[mp[i][j]]&&mark[i][j]!=1){                vis[mp[i][j]]=1;                t++;            }        }    }    return t;}void dfs(int x,int y,int val){    mark[x][y]=1;    for(int i=0;i<4;i++){        int nx=x+dx[i];        int ny=y+dy[i];        if(nx<1||nx>n||ny<1||ny>n||mark[nx][ny]==1) continue;        mark[nx][ny]=2;        if(mp[nx][ny]==val) dfs(nx,ny,val);        }}inline int fill(int x){    int t=0;    for(int i=1;i<=n;i++){        for(int j=1;j<=n;j++){            if(mark[i][j]==2&&mp[i][j]==x){                t++;                dfs(i,j,x);            }        }    }    return t;}void search(int k){    int v=get();    if(!v) ans=1;    if(k+v>s||ans) return ;    int tmp[N][N];    for(int i=0;i<=5;i++){        memcpy(tmp,mark,sizeof mark);        if(fill(i)) search(k+1);        memcpy(mark,tmp,sizeof mark);    }}int main(){    freopen("sh.txt","r",stdin);    for(scanf("%d",&n);n;scanf("%d",&n)){        memset(mark,0,sizeof mark);        ans=0;        for(int i=1;i<=n;i++){            for(int j=1;j<=n;j++){                scanf("%d",&mp[i][j]);            }        }        dfs(1,1,mp[1][1]);        for(s=0;;s++){            search(0);            if(ans){printf("%d\n",s);break;}        }    }    return 0;}

 

Codevs 2495 水叮当的舞步