UVA1505 Flood-it!

算法竞赛 算法-搜索
编辑文章

题意

给出一个 $N\times N$ 的矩形,每个格子染有 $0..5$ 这 $6$ 种颜色。每次操作可以把左上角格子所在的同色联通块全部染成一种颜色,求把所有格子染成一种颜色至少需要多少种操作?

$N\le 8$ 。多组数据,组数 $T\le 20$ 。

题解

朴素的搜索为每次枚举要染的颜色,染色后继续搜索直到染完。退出的条件为某次染色没有新增的格子。期望得分30。

#include<bits/stdc++.h>

using namespace std;

inline int read()
{
    char ch=getchar();
    int f=1,x=0;
    while (ch<'0' || ch>'9')
    {
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return f*x;
}

int n,ans,s[10][10];
const int fx[4]={0,0,1,-1},fy[4]={1,-1,0,0};

void work(int x,int y,int c,int &cnt,int col)
{
    cnt++;
    s[x][y]=c;
    for (int i=0;i<4;i++)
    {
        int tx=x+fx[i],ty=y+fy[i];
        if (tx<1 || tx>n || ty<1 || ty>n) continue;
        if (s[tx][ty]!=col) continue;
        work(tx,ty,c,cnt,col);
    }
}

inline bool check()
{
    for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if (s[i][j]!=s[1][1]) return 0;
    return 1;
}

void dfs(int col,int cur,int last)
{
    if (col==s[1][1]) return;
    if (cur>=ans) return;
    if (check()) return (void)(ans=cur);
    bool vis2[10][10]; int s2[10][10];
    memcpy(s2,s,sizeof(s));
    int cnt=0;
    work(1,1,col,cnt,s[1][1]);
    if (cnt<=last) goto back;
    for (int i=0;i<6;i++) if (i!=col) dfs(i,cur+1,cnt);
    back:
    memcpy(s,s2,sizeof(s));
}

signed main()
{
    while (~scanf("%d",&n) && n)
    {
        ans=1e9;
        for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) s[i][j]=read();
        for (int i=0;i<6;i++) dfs(i,0,0);
        printf("%d\n",ans);
    }
    return 0;
}

显然每次从 $(1,1)$ 开始重新染色的时间开销过大,考虑只维护新增的格子。

对所有格子在 $(1,1)$ 所在联通块的情况,使用新标记 $col[x]$ 记录。如果在则 $col[x]=1$ ;如果在联通块的边缘,即下一步就染的 $col[x]=2$ ;否则 $col[x]=0$ 。每次染色枚举 $col[x]=2$ 的格子即可。

还需要使用迭代加深优化。剪枝就是当前深度 $+$ 不在联通块的格子的颜色数 $>$ 最大深度则退出。显然剩下的格子有多少种颜色就至少要做几次。

#include<bits/stdc++.h>

using namespace std;

inline int read()
{
    char ch=getchar();
    int f=1,x=0;
    while (ch<'0' || ch>'9')
    {
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return f*x;
}

int n,ans,s[10][10],col[10][10];
const int fx[4]={0,0,1,-1},fy[4]={1,-1,0,0};

inline void work(int,int,int);
inline bool check(int);
inline int left();
bool dfs(int);

signed main()
{
    while (~scanf("%d",&n) && n)
    {
        memset(col,0,sizeof(col));
        for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) s[i][j]=read();
        work(1,1,s[1][1]); //先对 (1,1) 染色
        for (ans=0;;ans++) if (dfs(0)) {printf("%d\n",ans); break; }
    }
    return 0;
}

bool dfs(int dep) //主过程
{
    int l=left();
    if (!l) return 1;
    if (dep+l>ans) return 0;
    int col2[10][10];
    for (int i=0;i<6;i++)
    {
        memcpy(col2,col,sizeof(col));
        if (check(i) && dfs(dep+1)) return 1;
        memcpy(col,col2,sizeof(col));
    }
    return 0;
}

inline int left() //剩余的颜色数
{
    bool ccnt[6]; memset(ccnt,0,sizeof(ccnt));
    int res=0;
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            if (col[i][j]==1 || ccnt[s[i][j]]) continue;
            ccnt[s[i][j]]=1;
            res++;
        }
    }
    return res;
}

inline bool check(int x) //检查某种颜色是否能染
{
    bool res=0;
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            if (col[i][j]!=2 || s[i][j]!=x) continue;
            work(i,j,x);
            res=1;
        }
    }
    return res;
}

inline void work(int x,int y,int c) //染色
{
    col[x][y]=1;
    for (int i=0;i<4;i++)
    {
        int tx=x+fx[i],ty=y+fy[i];
        if (tx<1 || tx>n || ty<1 || ty>n || col[tx][ty]==1) continue;
        col[tx][ty]=2;
        if (s[tx][ty]==c) work(tx,ty,c);
    }
}

新评论

称呼不能为空
邮箱格式不合法
网站格式不合法
内容不能为空