围豆豆,题解

题目直接点链接

题意:

  走一条从起点回到起点的路径,使得围住的豆豆的权值之和减去步数最大。(封闭叫围住)

分析:

  围住的概念还是看看原题,上面有图,挺好理解的。然后就是处理了,这题的数据范围很小,可枚举的东西很多,我们可以分别试一试,首先先想一想,路线可以如何枚举,路线都枚举一边这个好像不是很好操作,而且路线也稍微有点多,我们要想一个别的办法,其实10*10还是比较小的数据的,还是可以考虑暴力一点的遍历,但是操作要方便一些,于是我们可以比较简单的想到Bfs,可是怎么Bfs呢?毕竟一个点可能不止到一次,不能说到过某个点就不再去了,而且不加限制又会不停的Bfs下去,跳不出循环。所有我们要考虑一种更加可靠的办法限制。

  怎么加这个限制呢?我们比较好想到的是走过相同的格子到同一个格子的continue掉,首先格子数:10*10,要开一个2的100次方的数组,不行,而且其实这个方法也不对(看看题目中的图)。那怎么办呢?看到题目中的豆豆选择方案也是可以枚举的,所有我们考虑枚举,但是怎么枚举呢?我们要定义一个和豆豆是不是在圈子里的状态,所有我们要找到一种状态可以确定/影响豆豆是不是在圈子里,我们只要考虑在最后时(即回到起点时)会怎样?先想比较好判断的,比较一般的情况,这个点如果右边(当然上下左右都可以)有奇数根线线,那么就会被包在圈子里。这是为什么呢,可以画一画图想一想(其实挺简单的,假如这个点从右边走到这里,如果右边有1根线,即点进入了图形,有两个就是进入又出去,有三个就是进去出去进去,所以有奇数个时,点在里面,有偶数个时,点不在里面)。这个貌似挺好的,但是处理的时候要注意,我们的空间是不连续的,或者说我们不能直接找右边的走过的格子数,因为会出现一条很长的横向边,如图,其中#表示边,1表示豆豆。

                        

明明是类似的在圈子里的情况,可是判断一个在圈子里,一个不在。

那么怎么解决这个问题呢?

其实,遇到这种情况我们可以直接把一堆连续的看作一个就好了,当然如果上去就看作没有,但是怎么看呢,因为我们要进行Bfs,不太好判断是怎么来的,所有我们就这样,如果这个点在一串最下面,那么忽略掉(当然只有一竖直着只有一个连续的点也是最下面)。然后再对比着图看看。

好的,现在我们知道了什么会影响到豆豆是不是在圈子里,然后就是怎么去限制Bfs,其实还是很简单的,我们找一个vis数组,如果vis[i][j][k]==1(其中i,j表示坐标,k表示状态),就不再去Bfs了。那这个k怎么定义呢,根据刚刚的叙述,你会发现如果每个豆子右边的边数奇偶都一样时,到达过一边就不要再去了,这个应该没有什么问题。那么我们只需让k的第i(二进制)位表示第i个豆子的右边的边的奇偶,说到这里,应该就没什么问题了吧,当然,我们还要枚举起点,算算复杂度,还是可以接受的,所有这一题就解决了。

然后细节见代码。

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
struct Node{//Bfs需要
    int x;
    int y;
    int s;
    int bs;//到次Node走的步数
    Node(){
    }
    Node(int a,int b,int c,int d){
        x=a;
        y=b;
        s=c;
        bs=d;
    }
};
queue<Node> qu;
bool vis[15][15][(1<<9)+10];
int Min[(1<<9)+10];//记录走到状态i所需最少步数
int d[15][15];//记录可不可以走
int vald[15];
int yh[15][15];//确定某点影响的豆子
int dx[4]={-1,1,0,0};//Bfs用
int dy[4]={0,0,1,-1};
int n,m;
void Solve(int xx,int yy){
    memset(vis,0,sizeof(vis));
    qu.push(Node(xx,yy,0,0));
    vis[xx][yy][0]=1;
    while(!qu.empty()){
        Node js=qu.front();
        qu.pop();
        for(int i=0;i<4;i++){
            int x=js.x+dx[i],y=js.y+dy[i],s=js.s,bs=js.bs+1;
            if(i==0)//向上走
                s^=yh[x][y];
            if(i==1)//向下走
                s^=yh[js.x][js.y];
            if(x>0&&y>0&&x<=n&&y<=m&&d[x][y]==0&&vis[x][y][s]==0){
                qu.push(Node(x,y,s,bs));
                vis[x][y][s]=1;
                if(x==xx&&y==yy)
                    Min[s]=min(Min[s],bs);
            }
        }
    }
}
int F(int a){//找到状态a的豆子权值之和
    int ans=0;
    int i=1;
    while(a){
        if(a&1)
            ans+=vald[i];
        i++;
        a>>=1;
    }
    return ans;
}
int main(){
    memset(Min,0x3f,sizeof(Min));
    int ds;
    scanf("%d%d%d",&n,&m,&ds);
    for(int i=1;i<=ds;i++)
        scanf("%d",&vald[i]);
    char js; 
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf(" %c",&js);
            if(js=='#')
                d[i][j]=-1;
            else{
                d[i][j]=js-'0';
                if(d[i][j])
                    for(int k=j+1;k<=m;k++)
                        yh[i][k]|=(1<<(d[i][j]-1));
            }
        }
    for(int i=1;i<=n;i++)//枚举起点
        for(int j=1;j<=m;j++)
            Solve(i,j);
    int ans=0;
    for(int i=0;i<=(1<<9)-1;i++)
        ans=max(ans,F(i)-Min[i]);
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/wish-all-ac/p/12697156.html