jzoj4016 [雅礼联考DAY01]圈地为王 搜索+状压

66 篇文章 0 订阅

Description


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

Solution


一开始看重要格子这么少以为是搜索然后最小割什么的

一个重要思想就是从点往上下左右四个方向做射线,如果与图形的交点数量是奇数说明在图形内
在这题里我们可以用10位二进制状态s记录每个重要点上方交点情况的奇偶性,用dis[x][y][s]表示走到(x,y)状态为s的最短路,然后统计dis[0][0]即可

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define fi first
#define se second

typedef std:: pair <int,int> pair;
typedef std:: pair <int,pair> bair;
const int INF=0x7fffffff;

std:: queue <bair> que;
std:: vector <pair> vec;

pair p[50005],dir[4]={pair(-1,0),pair(1,0),pair(0,-1),pair(0,1)};

int dis[52][52][2049],size;
int rc[52][52],ans[235],n,m;

bool vis[52][52][2049];

char str[52];

int get_S(int x,int y,int s,int k) {
    int ret=s;
    for (int ti=0;ti<size;ti++) {
        pair i=vec[ti];
        if (i.se==y&&k==3&&i.fi>x||i.se==y+1&&k==2&&i.fi>x) {
            ret^=(1<<ti);
        }
    }
    return ret;
}

void bfs() {
    fill(dis,63);
    dis[0][0][0]=0; vis[0][0][0]=1;
    que.push(bair(0,pair(0,0)));
    int x,y,s,S;
    for (;!que.empty();) {
        bair front=que.front(); que.pop();
        x=front.fi; y=front.se.fi;
        s=front.se.se;
        pair now(x,y);
        rep(k,0,3) {
            pair tar=pair(now.fi+dir[k].fi,now.se+dir[k].se);
            S=s; if (k>1) S=get_S(tar.fi,tar.se,s,k);
            if (tar.fi>=0&&tar.fi<=n&&tar.se>=0&&tar.se<=m) {
                if (dis[x][y][s]+1<dis[tar.fi][tar.se][S]) {
                    dis[tar.fi][tar.se][S]=dis[x][y][s]+1;
                    if (!vis[tar.fi][tar.se][S]) {
                        vis[tar.fi][tar.se][S]=1;
                        que.push(bair(tar.fi,pair(tar.se,S)));
                    }
                }
            }
        }
        vis[x][y][s]=0;
    }
}

int main(void) {
    freopen("data.in","r",stdin);
    while (~scanf("%s",str+1)) {
        n++; m=strlen(str+1);
        rep(i,1,m) {
            if (str[i]=='X') rc[n][i]=1;
            else if (str[i]=='I') rc[n][i]=2;
            if (str[i]!='.') vec.push_back(pair(n,i));
        }
    } size=vec.size();
    bfs(); int mx=0;
    fill(ans,63);
    rep(s,1,-1+(1<<size)) {
        int cnt=0;
        for (int tj=0;tj<size;tj++) {
            pair j=vec[tj];
            if ((1<<tj)&s) {
                if (rc[j.fi][j.se]==2) cnt++;
                else {cnt=-1; break;}
            }
        }
        if (cnt==-1) continue;
        ans[cnt]=std:: min(dis[0][0][s],ans[cnt]);
        mx=std:: max(mx,cnt);
    }
    rep(i,1,mx) printf("%d\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值