bzoj1433: [ZJOI2009]假期的宿舍
二分图匹配。
每个点分为俩个点0和1,表示有床的人和要睡觉的人。跑最大流。
图中所有边的流量均为1
1.S向每个有床的人(0)连一条边。
2.每个不回家的人和校外的人(1)向T连一条边。
3.每个有床的人和自己连一条边(0->1)。
4.每个认识的人连一条边(0->1).
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 10000 + 10; const int maxm = 20000 + 10; const int inf = 0x3f3f3f3f; int g[maxn],v[maxm],next[maxm],f[maxm],eid; int id[maxn][2]; int n,S,T,cnt,vid; int a[maxn],b[maxn]; int dist[maxn],gap[maxn]; void addedge(int a,int b,int F) { v[eid]=b; next[eid]=g[a]; f[eid]=F; g[a]=eid++; v[eid]=a; next[eid]=g[b]; f[eid]=0; g[b]=eid++; } void build() { memset(g,-1,sizeof(g)); eid=0; cnt=0; vid=0; scanf("%d",&n); for(int i=1;i<=n;i++) { id[i][0]=++vid; id[i][1]=++vid; } S=++vid; T=++vid; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); if(a[i]) addedge(S,id[i][0],1); } for(int i=1;i<=n;i++) { scanf("%d",&b[i]); if(!a[i] || !b[i]) { cnt++; addedge(id[i][1],T,1); } } for(int i=1,t;i<=n;i++) for(int j=1;j<=n;j++) { scanf("%d",&t); if(a[i] && t) addedge(id[i][0],id[j][1],1); if(a[i] || i==j) addedge(id[i][0],id[i][1],1); } } int ISAP(int u,int flow) { if(u==T) return flow; int cur=0,aug,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; } void solve() { int res=0; memset(dist,0,sizeof(dist)); gap[0]=vid; while(dist[S]<vid) res+=ISAP(S,inf); printf(res==cnt?"^_^\n":"T_T\n"); } int main() { int T; scanf("%d",&T); while(T--) { build(); solve(); } return 0; }