网络流拆点建模。
注意二分查找的边界
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 500 + 10; const int maxm = 40000 + 10; const int INF = 0x3f3f3f3f; int g[maxn],v[maxm],next[maxm],f[maxm],eid; int S,T,vid; int id[maxn][2][2]; char m[60][60]; int n,k; int dist[maxn],gap[maxn]; void addedge(int a,int b,int c) { v[eid]=b; f[eid]=c; next[eid]=g[a]; g[a]=eid++; v[eid]=a; f[eid]=0; next[eid]=g[b]; g[b]=eid++; } void build(int x) { memset(g,-1,sizeof(g)); eid=0; for(int i=1;i<=n;i++) { addedge(S,id[i][0][0],x); addedge(id[i][0][0],id[i][0][1],k); addedge(id[i][1][1],id[i][1][0],k); addedge(id[i][1][0],T,x); } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(m[i][j]=='Y') addedge(id[i][0][0],id[j][1][0],1); else addedge(id[i][0][1],id[j][1][1],1); } } int ISAP(int u,int flow) { if(u==T) return flow; int aug,cur=0,mindist=vid; for(int i=g[u];~i;i=next[i]) if(f[i]&& dist[v[i]]+1==dist[u]) { aug=ISAP(v[i],min(flow-cur,f[i])); f[i]-=aug; f[i^1]+=aug; cur+=aug; if(cur==flow || dist[S]>=vid) { return cur; } } if(cur==0) { if(!--gap[dist[u]]) { dist[S]=vid; return cur; } for(int i=g[u];~i;i=next[i]) if(f[i]) mindist=min(mindist,dist[v[i]]); ++gap[dist[u]=mindist+1]; } return cur; } int max_flow() { memset(gap,0,sizeof(gap)); memset(dist,0,sizeof(dist)); gap[0]=vid; int res=0; while(dist[S]<vid) res+=ISAP(S,INF); return res; } bool check(int x) { build(x); return (max_flow()==x*n); } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%s",m[i]+1); for(int x=0;x<=1;x++) for(int y=0;y<=1;y++) id[i][x][y]=++vid; } S=++vid; T=++vid; int l=0,r=n,mid; while(l+1<r) { mid=(l+r)>>1; if(check(mid)) l=mid; else r=mid-1; } printf("%d\n",check(r)?r:l); return 0; }