题目
在\(n\)行\(m\)列的网格中,你要圈一些地。
你从左上角出发,最后返回左上角,路径内部的区域视为被你圈住。 你不可以进入网格内部, 只能在边上行走。 你的路径不能在左上角以外自交, 但是边足够宽, 你可以重复经过而不自交。
网格中有一些格子对你很重要,你要尽量圈住它;而另一些格子对你有坏处,你不能圈住它。
求圈住\(i\)个重要的格子的最小路径长度。(\(n,m\leq 50,Special Block\leq 10\))
分析
自交除了左上角完全可以被等价的方案代替,所以可以忽略它,接着如何保证好的被圈,坏的不被圈,
把一个点向任意方向引一条射线,如果这条线穿过奇数次多边形,那么点在多边形内,否则点在多边形外,
所以可以判断竖着的边(横着的边也行),然后记录选取的状态跑SPFA,再深搜选取
代码
#include <cstdio> #include <queue> #include <cstring> #define rr register using namespace std; struct rec{int x,y,S;}; queue<rec>q; const int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1}; int spx[11],spy[11],n,m,cnt,po[11],dis[51][51][1024],ans[11]; bool v[51][51][1024]; char a[51][51]; inline signed min(int a,int b){return a<b?a:b;} inline signed Turn_S(int x,int y,int SS){ for (rr int i=0;i<cnt;++i) if (spx[i]>x&&spy[i]==y) SS^=po[i]; return SS; } inline void dfs(int SS,int dep,int now){ ans[now]=min(ans[now],dis[0][0][SS]); if (dep==cnt) return; if (a[spx[dep]][spy[dep]]=='I') dfs(SS|po[dep],dep+1,now+1); dfs(SS,dep+1,now); } signed main(){ po[0]=1; rr char c; for (rr int i=1;i<10;++i) po[i]=po[i-1]+po[i-1]; for (n=1;(c=getchar())!=EOF;++n){ for (m=0;c=='.'||c=='I'||c=='X';c=getchar()){ a[n][++m]=c; if (c!='.') spx[cnt]=n,spy[cnt]=m,++cnt; } } memset(dis,42,sizeof(dis)); dis[0][0][0]=0,--n,v[0][0][0]=1,q.push((rec){0,0,0}); while (q.size()){ rr rec t=q.front(); rr int now=dis[t.x][t.y][t.S]; q.pop(); for (rr int k=0;k<4;++k){ rr int zx=t.x+dx[k],zy=t.y+dy[k],SS=t.S; if (zx<0||zy<0||zx>n||zy>m) continue; if (k&1) SS=Turn_S(t.x,k==3?t.y:zy,SS); if (dis[zx][zy][SS]>now+1){ dis[zx][zy][SS]=now+1; if (!v[zx][zy][SS]){ v[zx][zy][SS]=1; q.push((rec){zx,zy,SS}); } } } v[t.x][t.y][t.S]=0; } memset(ans,42,sizeof(ans)); dfs(0,0,0); for (rr int i=1;i<=cnt;++i) if (ans[i]<4e5) printf("%d\n",ans[i]); return 0; }
来源:https://www.cnblogs.com/Spare-No-Effort/p/12286634.html