题目直接点链接
题意:
走一条从起点回到起点的路径,使得围住的豆豆的权值之和减去步数最大。(封闭叫围住)
分析:
围住的概念还是看看原题,上面有图,挺好理解的。然后就是处理了,这题的数据范围很小,可枚举的东西很多,我们可以分别试一试,首先先想一想,路线可以如何枚举,路线都枚举一边这个好像不是很好操作,而且路线也稍微有点多,我们要想一个别的办法,其实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; }