题解:B3791 [信息与未来 2023] 电路布线

· · 题解

题目传送门

闲话:过了这道题后的好几个月,突然有人来问我这道题的解法,于是就有了这篇题解。

题意

给定一块 n\times m 的方格,其中一些格子必须铺线,一些格子不能铺线,另一些可铺可不铺。要求:

题解

这道题大概有以下几种方法:

  1. 超纲并且难写的轮廓线DP
  2. 正解但是难写的折半搜索
  3. 难写的随机乱搞做法

反正都很难写就对了。

我们从起点开始走起。

40分做法:

爆搜即可。枚举每一个方格内放/不放电线,并在最终进行判断是否合法。我们需要实现一个搜索函数和一个判断电线合法的函数:

bool dfs2(int x, int y, int prex, int prey){
    if(vis[x][y]) return 0;//有回路
    --cnt, vis[x][y]=1;
    for(auto [tx, ty]:d){
        tx+=x, ty+=y;
        if(tx==prex && ty==prey || tx<1 || ty<1 || tx>n || ty>m || t[tx][ty]!='+') continue;
        if(!dfs2(tx, ty, x, y)) return 0;
    }
    return 1;
}

void dfs(int x, int y, int tot){
    if(y==m+1) ++x, y=1;
    memset(vis, 0, sizeof vis);
    if(x==n+1){
        cnt=tot;//cnt表示总电线数减去搜索到的电线数;若cnt!=0,则该图不连通
        bool flag=0;
        if(tot>res && dfs2(sx, sy, -1, -1) && !cnt){
            memcpy(ans, t, sizeof t);
            res=tot;
        }
        return;
    }
    if(s[x][y]!='#'){
        t[x][y]='+';
        dfs(x, y+1, tot+1);
        t[x][y]=s[x][y];
    } 
    if(s[x][y]!='+') dfs(x, y+1, tot);
}

100分做法:

观察上述程序,我们发现有很多地方可以剪枝优化:

改良后的代码:

bool dfs2(int x, int y, int prex, int prey){
    if(vis[x][y]) return 0;
    --cnt, vis[x][y]=1;
    for(auto [tx, ty]:d){
        tx+=x, ty+=y;
        if(tx==prex && ty==prey || tx<1 || ty<1 || tx>n || ty>m || t[tx][ty]!='+') continue;
        if(!dfs2(tx, ty, x, y)) return 0;
    }
    return 1;
}

void dfs(int x, int y, int tot){
    if(y==m+1) ++x, y=1;
    if(0.8*((n-x)*m+m-y+1)+tot<=res) return;//1
    memset(vis, 0, sizeof vis);
    if(x==n+1){
        cnt=tot;
        bool flag=0;
        if(dfs2(sx, sy, -1, -1) && !cnt){
            memcpy(ans, t, sizeof t);
            res=tot;
        }
        return;
    }
    if(s[x][y]!='#'){
        t[x][y]='+';
        if(dfs2(x, y, -1, -1)) //2
            dfs(x, y+1, tot+1);
        t[x][y]=s[x][y];
    } 
    if(s[x][y]!='+') dfs(x, y+1, tot);
}

于是,我们就用简洁好写的剪枝搜索通过了这道题。