正题
题目链接:https://jzoj.net/senior/#contest/show/3011/1
题目大意
n∗mn*mn∗m的格子,格子之间有道路,对于每个iii就走过最短的回路使得
- 圈住iii个有价值的格子
- 没有圈住任何一个坏格子
解题思路
判断一个点是否在一个多边形内,我们可以往任何一个方向画一条射线,如果与多边形的交点为奇数那么就在,否则就不在。
那么我们考虑状态压缩fi,j,sf_{i,j,s}fi,j,s表示走到(i,j)(i,j)(i,j)这个位置,往上画线的交点为奇数的有用点格子集合为sss。
然后bfsbfsbfs转移即可。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define lowbit(x) (x&-x)
using namespace std;
const int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
struct point{int x,y,s;
}val[50],no[50];
queue<point> q;
int n,m,cnt1,cnt2,ans[50],f[55][55][1500];
char c[55];
int count_one(int x)
{int ans=0;while(x){ans++;x-=lowbit(x);}return ans;
}
int main()
{freopen("data.txt","r",stdin);cnt1=cnt2=-1;while(scanf("%s",c+1)!=EOF){n++;m=strlen(c+1);for(int j=1;j<=m;j++){if(c[j]=='I')val[++cnt1]=(point){n,j};if(c[j]=='X')no[++cnt2]=(point){n,j};}}memset(f,0x3f,sizeof(f));f[0][0][0]=0;q.push((point){0,0,0});while(!q.empty()){int x=q.front().x,y=q.front().y,s=q.front().s;q.pop();for(int k=0;k<4;k++){int zx=x+dx[k],zy=y+dy[k];if(zx<0||zx>n||zy<0||zy>m) continue;int tmp=s;if(k==1){for(int j=0;j<=cnt1;j++)if(val[j].y==y+1&&val[j].x>=x+1) tmp^=(1<<j); for(int j=0;j<=cnt2;j++)if(no[j].y==y+1&&no[j].x>=x+1) tmp^=(1<<j+cnt1+1); }if(k==3){for(int j=0;j<=cnt1;j++)if(val[j].y==y&&val[j].x>=x+1) tmp^=(1<<j); for(int j=0;j<=cnt2;j++)if(no[j].y==y&&no[j].x>=x+1) tmp^=(1<<j+cnt1+1); }if(f[x][y][s]+1<f[zx][zy][tmp]){q.push((point){zx,zy,tmp});f[zx][zy][tmp]=f[x][y][s]+1;}}}memset(ans,0x3f,sizeof(ans));cnt1++;for(int i=0;i<(1<<cnt1);i++){int d=count_one(i);ans[d]=min(ans[d],f[0][0][i]);}for(int i=1;i<=cnt1;i++)printf("%d\n",ans[i]);
}