#搜索,计算几何#JZOJ 4016 圈地为王 From 2020.02.07【NOIP提高组】模拟A 组

题目

\(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;
}

猜你喜欢

转载自www.cnblogs.com/Spare-No-Effort/p/12286634.html