【题解】ZJOI2009 假期的宿舍 网络流 最大流
好久没有来写博客啦,来水一发。
网络流建模
首先很容易想到,如果一个人能睡一张床,那么在这个人和这张床之间连接一条容量为1的边
从s向每个需要住宿的人连容量为1的边,表示这个人需要住宿
从每张床向t连容量为1的边,表示这个床容纳了一个人
求最大流,如果流量等于需要住宿的人数,则有解,否则无解
分析一下,一共需要2*n+2个点,n*n+2*n条有向边
代码如下:
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 #include <cstdio> 5 #include <cmath> 6 #include <cstdlib> 7 #include <cassert> 8 #include <cctype> 9 #include <queue> 10 11 using namespace std; 12 const int MAXN = 55; 13 const int INF = 0x3f3f3f3f; 14 15 int n, sch[MAXN], home[MAXN]; 16 17 template< int MAXN, int MAXSZ > 18 struct List { 19 int head[MAXN], nxt[MAXSZ], val[MAXSZ], lidx; 20 List() { clear(); } 21 void clear() { 22 lidx = 0; 23 memset( head, -1, sizeof(head) ); 24 } 25 void ins( int n, int v ) { 26 val[lidx] = v; nxt[lidx] = head[n]; head[n] = lidx++; 27 } 28 }; 29 30 template< int MAXV, int MAXE > 31 struct Dinic { 32 struct Edge { 33 int from, to, cap, flow; 34 Edge(){} 35 Edge( int from, int to, int cap, int flow ): 36 from(from),to(to),cap(cap),flow(flow){} 37 }edge[MAXE<<1]; int eidx; 38 int s, t; 39 List<MAXV,MAXE<<1> lst; 40 Dinic() { init(); } 41 void init() { 42 lst.clear(); eidx = 0; 43 } 44 void adde( int from, int to, int cap ) { 45 edge[eidx] = Edge(from,to,cap,0); 46 lst.ins(from,eidx++); 47 edge[eidx] = Edge(to,from,0,0); 48 lst.ins(to,eidx++); 49 } 50 queue<int> bfsq; bool vis[MAXV]; 51 int dist[MAXV]; 52 bool bfs() { 53 memset( vis, false, sizeof(vis) ); 54 bfsq.push(s); vis[s] = true; dist[s] = 0; 55 while( !bfsq.empty() ) { 56 int u = bfsq.front(); bfsq.pop(); 57 for( int i = lst.head[u]; ~i; i = lst.nxt[i] ) { 58 Edge &e = edge[lst.val[i]]; int v = e.to; 59 if( !vis[v] && e.cap > e.flow ) { 60 vis[v] = true; dist[v] = dist[u] + 1; 61 bfsq.push(v); 62 } 63 } 64 } return vis[t]; 65 } 66 int cur[MAXV]; 67 int dfs( int u, int res ) { 68 if( u == t || res == 0 ) return res; 69 int flow = 0; 70 if( cur[u] == INF ) cur[u] = lst.head[u]; 71 for( int &i = cur[u]; ~i; i = lst.nxt[i] ) { 72 Edge &e = edge[lst.val[i]]; int v = e.to; 73 if( e.cap > e.flow && dist[v] == dist[u] + 1 ) { 74 int nxtf = dfs( v, min( res, e.cap-e.flow ) ); 75 if( nxtf == 0 ) continue; 76 flow += nxtf; res -= nxtf; 77 e.flow += nxtf; edge[lst.val[i]^1].flow -= nxtf; 78 if( res == 0 ) break; 79 } 80 } return flow; 81 } 82 int solve( int s, int t ) { 83 int flow = 0; 84 this->s = s; this->t = t; 85 while( bfs() ) { 86 memset( cur, 0x3f, sizeof(cur) ); 87 flow += dfs(s,INF); 88 } return flow; 89 } 90 }; 91 Dinic< MAXN<<1, MAXN*(MAXN+2) > dinic; 92 93 // j表示点的类别 94 // 0表示人,1表示床,2表示s和t 95 inline int id( int i, int j ) { 96 return i+j*n; 97 } 98 99 int main() { 100 int T; scanf( "%d", &T ); 101 while( T-- ) { 102 scanf( "%d", &n ); dinic.init(); 103 int need = n; // 需要住宿的人数 104 int s = id(1,2), t = id(2,2); 105 for( int i = 1; i <= n; ++i ) { 106 scanf( "%d", sch+i ); 107 if( sch[i] ) dinic.adde( id(i,1), t, 1 ); // 每个在校生都有床 108 } 109 for( int i = 1; i <= n; ++i ) { 110 scanf( "%d", home+i ); 111 if( !sch[i] ) home[i] = -1; // 非在校生不存在回不回家 112 if( home[i] == 1 ) need--; // 不需要住宿 113 else dinic.adde( s, id(i,0), 1 ); // 需要住宿 114 } 115 for( int i = 1; i <= n; ++i ) for( int j = 1; j <= n; ++j ) { 116 int rela; scanf( "%d", &rela ); 117 if( home[i] == 1 ) continue; // 回家的人不需要住宿 118 if( sch[i] && i == j ) dinic.adde( id(i,0), id(j,1), 1 ); // 在校生可以睡自己的床 119 else if( sch[j] && rela ) dinic.adde( id(i,0), id(j,1), 1 ); // 可以睡别人的床 120 } 121 int flow = dinic.solve(s,t); 122 if( flow == need ) printf( "^_^\n" ); 123 else printf( "T_T\n" ); 124 } 125 return 0; 126 }