【传送门:BZOJ2150】
简要题意:
给出一个矩阵,矩阵上的字符有两种,一种是'x',表示山洞(不可走),一种是'.',表示城镇
可以在城镇处放士兵,士兵经过的每个城镇都会被占领,士兵只能向下走,而且行走的方式和马相似,不过马走的是1*2,士兵走的是R*C,士兵不能经过一个被占领的城镇
求出最少派出多少个士兵能够占领所有城镇
题解:
最小路径覆盖问题,用二分图匹配
将所有城镇拆成两个集合
如果x能走到y,则x连向y的第二个集合的点
然后将总点数-最大匹配数就是最小路径覆盖数了
参考代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> using namespace std; char st[51][51]; struct node { int x,y,next; }a[21000];int len,last[6100]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int dx[5],dy[5]; int n,m,r,c; int p(int x,int y){return (x-1)*m+y;} int match[6100],chw[6100]; bool findmuniu(int x,int t) { for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(chw[y]!=t) { chw[y]=t; if(match[y]==0||findmuniu(match[y],t)==true) { match[y]=x; return true; } } } return false; } int main() { scanf("%d%d%d%d",&n,&m,&r,&c); for(int i=1;i<=n;i++) scanf("%s",st[i]+1); len=0;memset(last,0,sizeof(last)); dx[1]=r;dy[1]=-c;dx[2]=r;dy[2]=c; dx[3]=c;dy[3]=-r;dx[4]=c;dy[4]=r; int sum=0; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(st[i][j]=='.') { sum++; for(int k=1;k<=4;k++) { int tx=i+dx[k],ty=j+dy[k]; if(tx<1||ty<1||tx>n||ty>m||st[tx][ty]=='x') continue; ins(p(i,j),p(tx,ty)+n*m); } } } } memset(chw,0,sizeof(chw)); memset(match,0,sizeof(match)); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(st[i][j]=='.') { int t=p(i,j); if(findmuniu(t,t)==true) sum--; } } } printf("%d ",sum); return 0; }