水叮当的舞步 深搜

背景 Background

水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变。

为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~

描述 Description

地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色。

水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。

由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把所有格子的颜色变成一样的。

输入格式 InputFormat

每个测试点包含多组数据。

每组数据的第一行是一个整数N,表示地摊上的格子有N行N列。

接下来一个N*N的矩阵,矩阵中的每个数都在0~5之间,描述了每个格子的颜色。

N=0代表输入的结束。

输出格式 OutputFormat

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

样例输入 SampleInput 

2

0 0

0 0

3

0 1 2

1 1 2

2 2 1

0

样例输出 SampleOutput 

0

3

数据范围和注释 Hint

对于30%的数据,N<=5

对于50%的数据,N<=6

对于70%的数据,N<=7

对于100%的数据,N<=8,每个测试点不多于20组数据。



这题首先告诉我们,一定要审题,仔细审题,用心审题。

一开始看这道题,以为好难,要怎么怎么复杂地广搜。然后经过杨之典大触的指出后,我才知道其实每次只是动左上角那个联通块,于是问题就清晰了。

我们经过一定的思考,这题广搜其实没有深搜好用,于是我们就用深搜来解决这道题。

那么用深搜就一定要有一定的剪枝,所以我们来看看怎么剪枝。

首先第一个剪枝很明显,如果在ans步内已经搜到过目标,那么之后搜索的深度肯定在这个范围之内。

然后是第二个剪枝。假设我们现在图中有a中颜色,现在是b步,已经在ans步内搜到过答案,那么如果a+b>ans的话也没必要往下搜了,因为每次最多改变一种颜色。

第三个剪枝,假设我们左上角联通块周围的颜色是2,4,那么我们这时还要不要把左上角的联通块变成五个颜色往下搜呢?显然,我们只要把左上角的联通块变成2或4再往下搜,

变成其他颜色显然不是最优的。

第四个剪枝,是建设在第三个剪枝之上的,假设我们左上角的联通块周围的颜色是2,4,而且2有4个,4只有2个,那个我们先搜哪个呢?当然是先搜2,。

讲了这么多,感觉好麻烦,其实,真的很麻烦。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
#define il inline
#define db double
using namespace std;
il int gi()
{
    int x=0,y=1;
    char ch=getchar();
    while(ch<‘0‘||ch>‘9‘)
    {
        if(ch==‘-‘)
        y=-1;
        ch=getchar();
    }
    while(ch>=‘0‘&&ch<=‘9‘)
    {
        x=x*10+ch-‘0‘;
        ch=getchar();
    }
    return x*y;
}
il ll gl()
{
    ll x=0,y=1;
    char ch=getchar();
    while(ch<‘0‘||ch>‘9‘)
    {
        if(ch==‘-‘)
        y=-1;
        ch=getchar();
    }
    while(ch>=‘0‘&&ch<=‘9‘)
    {
        x=x*10+ch-‘0‘;
        ch=getchar();
    }
    return x*y;
}
int n;
int ans;
int map[10][10];
int pre[10][10];
bool v[10][10];
il bool check()//检查是否满足条件
{
    int now=map[1][1];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        if(map[i][j]!=now)
        return 0;
    return 1;
}
il void hzr()//记录修改前的图
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        pre[i][j]=map[i][j];
}
il void hzrr()//用来回溯
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        map[i][j]=pre[i][j];
}
il int count()//用来做第二个剪枝的函数
{
    int sum=0;
    bool vis[45];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        if(!vis[map[i][j]])
        {
            sum++;
            vis[map[i][j]]=1;
        }
    return sum;
}
struct col
{
    int co,s;
}c[10];//用来记录联通块周围的颜色种类和数量的结构体
bool cmp(col a,col b)
{
    return a.s>b.s;
}
bool vis[10][10];
il void bfs()//bfs求联通块,并且记录左上角的联通块周围的颜色
{
    int head=0,tail=1;
    int t[100][2];//用来广搜的队列
    for(int i=0;i<=5;i++)
    c[i].s=0,c[i].co=i;//初始化记录的结构体
    memset(vis,0,sizeof(vis));
    int dis[5]={0,1,0,-1,0};
    t[0][0]=1;
    t[0][1]=1;
    v[1][1]=1;
    vis[1][1]=1;
    while(head!=tail)
    {
        for(int i=0;i<4;i++)
        {
            int x=t[head][0]+dis[i],y=t[head][1]+dis[i+1];
            if(vis[x][y]||x>n||x<1||y>n||y<1)//如果在界外或者已是联通块里的
            continue;
            if(map[x][y]!=map[1][1])//如果是周围的不同颜色的就记录
            {
                c[map[x][y]].s++;
                continue;
            }
            vis[x][y]=1;
            t[tail][0]=x;
            t[tail++][1]=y;
        }
        head++;
    }
}
il void change(int x)//将联通块变成目标颜色
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        if(vis[i][j])
        map[i][j]=x;
}
viod dfs(int x)
{
    if(x>ans)//第一个剪枝
    return;
    if(check())//如果满足条件,那么就记录
    {
        if(ans>x)
        ans=x;
        return;
    }
    if(x+count()>ans)//第二个剪枝
    return;
    hzr();//为回溯做准备
    bfs();//求出联通块,并且记录联通块周围的颜色种类和数量
    sort(c,c+5,cmp);//按周围颜色出现的次数排序
    for(int i=0;i<=5;i++)
    {
        if(!c[i].s)
        break;
        change(c[i].co);//把联通块都变成目标颜色
        dfs(x+1);//往下搜
        hzrr();//回溯
    }
}
int main()
{
    while(1)
    {
        n=gi();
        if(!n)
        break;
        ans=2e8;
        memset(map,0,sizeof(map));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            map[i][j]=gi();
        dfs(0);
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-12-22 20:08:26

水叮当的舞步 深搜的相关文章

bzoj 3041: 水叮当的舞步 迭代加深搜索 &amp;&amp; NOIP RP++

3041: 水叮当的舞步 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 72  Solved: 44[Submit][Status] Description 水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变.为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色.水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步

【wikioi】2495 水叮当的舞步(A*+迭代加深搜索)

这题我还是看题解啊囧.(搜索实在太弱.完全没想到A*,还有看题的时候想错了,.,- -) 好吧,估价还是那么的简单,判断颜色不同的数目即可(左上角的联通块不算在内) 然后A*还是一样的做法. 迭代加深还是一样的味道- 在这里我们用c[i][j]来表示左上角开始的联通块和联通块外面一层(因为要从外面一层拓展颜色),分别记为1和2 那么我们在搜索的时候,染色只要染c[i][j]为2的颜色种类,并且更新联通块(在这里不需要传图,因为一层一层的拓展下去的话,是单调递增的,所以用不到之前的颜色)我们在搜索

BZOJ 3041 水叮当的舞步

3041: 水叮当的舞步 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 120  Solved: 67[Submit][Status][Discuss] Description 水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变.为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色.水叮当可以随意选择一个0~5之间的颜色

codevs 2495 水叮当的舞步

题目描述 Description 水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变. 为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~ 地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色. 水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色.这里连通定义为:两个格子有公共边,并且颜色相同. 由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她

「Poetize5」水叮当的舞步

Description 水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变. 为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~ 地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色. 水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色.这里连通定义为:两个格子有公共边,并且颜色相同. 由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少

【codevs2495】水叮当的舞步

题目描述 Description 水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变.为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~ 地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色.水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色.这里连通定义为:两个格子有公共边,并且颜色相同.由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道

水叮当的舞步

[题目描述] 地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色.水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色.这里连通定义为:两个格子有公共边,并且颜色相同.水叮当想知道最少要多少步才能把所有格子的颜色变成一样的. [输入描述] 每个测试点包含多组数据.每组数据的第一行是一个整数N,表示地摊上的格子有N行N列:接下来一个N*N的矩阵,矩阵中的每个数都在0~5之间,描述了每个格子的颜色:N=0代表输入的结

codevs2495 水叮当的舞步

题目描述 Description 水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变. 为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~ 地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色. 水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色.这里连通定义为:两个格子有公共边,并且颜色相同. 由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她

【NOIP2013模拟】水叮当的舞步

题目 水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变. 为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~ 地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色. 水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,地毯左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色.这里连通定义为:两个格子有公共边,并且颜色相同. 由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把