圈地问题java,【GMOJ4016】圈地为王(示例代码)

题目

题目链接:https://gmoj.net/senior/#main/show/4016

在 (n) 行 (m) 列的网格中,你要圈一些地。

你从左上角出发,最后返回左上角,路径内部的区域视为被你圈住。 你不可以进入网格内部, 只能在边上行走。 你的路径不能在左上角以外自交, 但是边足够宽, 你可以重复经过而不自交。

网格中有一些格子对你很重要,你要尽量圈住它;而另一些格子对你有坏处,你不能圈住它。

求圈住 (i) 个重要的格子的最小路径长度。

思路

这道题挺妙的。

考虑如果一个格子正上方的边经过了奇数次,那么这个点最终就被围住了,如果它上方的点经过了偶数次,那么就没有被围住。

由于特殊点(重要点+坏点)只有(10)个。考虑状压。

设(f[i][j][S])表示现在走到点((i,j)),每一个重要点的上方经过的次数的奇偶性为(S)的最少步数。

那么可以(bfs)转移,注意维护(S)即可。

时间复杂度(O(2^knm)),其中(k)表示特殊点的个数。

代码

#include

#include

#include

#include

using namespace std;

const int N=55,MAXN=1025,Inf=1e9;

const int dx[]={0,0,0,-1,1},dy[]={0,-1,1,0,0};

int n,m,cnt,Sbad,f[N][N][MAXN],S[N][N],id[N][N],ans[N];

char a[N][N];

queue qx,qy,qs;

void bfs()

{

memset(f,0x3f3f3f3f,sizeof(f));

f[1][1][0]=0;

qx.push(1); qy.push(1); qs.push(0);

while (qx.size())

{

int x=qx.front(),y=qy.front(),s=qs.front();

qx.pop(); qy.pop(); qs.pop();

for (int i=1;i<=4;i++)

{

int xx=x+dx[i],yy=y+dy[i],ss;

if (yy!=y) ss=s^S[x][min(y,yy)];

else ss=s;

if (xx<1 || yy<1 || xx>n || yy>m || f[xx][yy][ss]

f[xx][yy][ss]=f[x][y][s]+1;

qx.push(xx); qy.push(yy); qs.push(ss);

}

}

}

int main()

{

for (int i=1;scanf("%s",a[i]+1)>0;i++) n++;

m=strlen(a[1]+1)+1; n++;

for (int i=1;i

for (int j=1;j

if (a[i][j]!='.')

{

id[i][j]=++cnt;

if (a[i][j]=='X') Sbad|=(1<

for (int k=1;k<=i;k++)

S[k][j]|=(1<

}

bfs();

memset(ans,0x3f3f3f3f,sizeof(ans));

for (int i=1;i

if (!(i&Sbad))

{

int s=0;

for (int j=i;j;j>>=1) s+=(j&1);

ans[s]=min(ans[s],f[1][1][i]);

}

for (int i=1;ans[i]

printf("%d

",ans[i]);

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值