JZOJ 4016 圈地为王
Description
在 n 行 m 列的网格中,你要圈一些地。
你从左上角出发,最后返回左上角,路径内部的区域视为被你圈住。 你不可以进入网格内部, 只能在边上行走。 你的路径不能在左上角以外自交, 但是边足够宽, 你可以重复经过而不自交。
网格中有一些格子对你很重要,你要尽量圈住它;而另一些格子对你有坏处,你不能圈住它。
求圈住 i 个重要的格子的最小路径长度。
你从左上角出发,最后返回左上角,路径内部的区域视为被你圈住。 你不可以进入网格内部, 只能在边上行走。 你的路径不能在左上角以外自交, 但是边足够宽, 你可以重复经过而不自交。
网格中有一些格子对你很重要,你要尽量圈住它;而另一些格子对你有坏处,你不能圈住它。
求圈住 i 个重要的格子的最小路径长度。
考虑一个点,判其是否在一个区域内可以用射线法,这道题也一样,考虑每个关键点上面是否有奇数条线段。
这可以充分描述状态。
f[x][y][s] 为当前做到x y这个点,关键点的状态为s的情况。
#include<bits/stdc++.h> #define pii pair<int,int> #define xo first #define yo second #define N ((1<<11)+1) //#define @ WERTYUIODFGHJ using namespace std; struct QWERTYUI{ int x,y,s; QWERTYUI(){} QWERTYUI(int _x,int _y,int _s):x(_x),y(_y),s(_s){} }; queue <QWERTYUI> Q; char ch[139][139]; int n,m,pd[19],cnt,tot,dis[54][53][N]; bool vis[54][54][N]; pii t[19]; const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0}; int get(int S,int xx,int yy) { for (int i=1;i<=cnt;i++){ if (yy==t[i].yo&&xx<=t[i].xo) S^=(1<<i-1); } return S; } signed main () { while (~scanf("%s",ch[n++])); m=strlen(ch[0]); // n=3;//sxhdfghjikoldesfghjkdf for (int i=0;i<n;i++) for (int j=0;j<m;j++) { if (ch[i][j]=='X') { pd[++cnt]=0; t[cnt]=pii(i,j); } else if (ch[i][j]=='I') { pd[++cnt]=1; t[cnt]=pii(i,j); } } memset(dis,63,sizeof dis); int x,y,S; Q.push(QWERTYUI(0,0,0)); dis[0][0][0]=0; while (!Q.empty()){ QWERTYUI u=Q.front(); Q.pop(); vis[u.x][u.y][u.s]=0; for (int i=0;i<4;i++) { x=u.x+dx[i],y=u.y+dy[i]; if (x<0||y<0||x>n||y>m) continue; if (i==0||i==1) S=get(u.s,u.x,min(y,u.y)); else S=u.s; if (dis[x][y][S]>dis[u.x][u.y][u.s]+1) { dis[x][y][S]=dis[u.x][u.y][u.s]+1; if (vis[x][y][S]==0) { vis[x][y][S]=1; Q.push(QWERTYUI(x,y,S)); } } } } int ans[19],Ma=0; memset(ans,63,sizeof ans); for (int i=1;i<(1<<cnt);i++) { tot=0; for (int j=1;j<=cnt;j++) if (i&(1<<j-1)) { if (!pd[j]) {tot=-1; break;} else ++tot; } if (tot^-1) { Ma=max(tot,Ma); if(dis[0][0][i]<ans[tot]) ans[tot]=dis[0][0][i]; } } for (int i=1;i<=Ma;i++) printf("%d\n",ans[i]); return 0; }