JZOJ 4016 圈地为王

Description

在 n 行 m 列的网格中,你要圈一些地。
你从左上角出发,最后返回左上角,路径内部的区域视为被你圈住。 你不可以进入网格内部, 只能在边上行走。 你的路径不能在左上角以外自交, 但是边足够宽, 你可以重复经过而不自交。
网格中有一些格子对你很重要,你要尽量圈住它;而另一些格子对你有坏处,你不能圈住它。
求圈住 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;
}

 

posted @ 2018-07-13 14:52  泪寒之雪  阅读(292)  评论(0编辑  收藏  举报