题目传送门:https://vjudge.net/problem/UVA-1440
看上去很像DAG的最小路径覆盖QwQ?
反正我是写了一个上下界网络流,建模方法清晰易懂。
建立源$s$,向每个原图中的点连边,下界为$0$,上界为$infty$,表示在每个点可以放置无限多的人。
建立汇$t$,每个原图中的点向汇连边,下界为$0$,上界为$infty$,表示人可以在任意一个点停止滑雪。
对于原图中的每条弧$<u,v>$,连边$<u,v>$,下界为$1$,上界为$infty$,表示这条路至少要被检查一遍。
然后跑一个有源汇上下界最小流就好啦OvO。
不会求上下界网络流的看这里:http://www.cnblogs.com/mlystdcall/p/6734852.html
输出方案?瞎搞QwQ。任选一个“需要放置人的点”开始dfs,在每个点的出边中任选一个“还需要被访问的”继续dfs,详见代码和注释。
这样输出方案为什么是对的?时间太久远辣我已经忘辣QwQ。
代码如下:
1 #include <algorithm> 2 #include <cstring> 3 #include <cstdio> 4 #include <queue> 5 6 using namespace std; 7 const int INF = 0x3f3f3f3f; 8 const int MAXN = 110; 9 10 namespace ISAP { 11 const int MAXV = MAXN; 12 const int MAXE = ( MAXV*MAXV/2 + MAXV*2 )*3; 13 14 struct Edge { 15 int u, v, c, f; 16 Edge(){} 17 Edge( int u, int v, int c, int f ): 18 u(u),v(v),c(c),f(f){} 19 }edge[MAXE<<1]; 20 int n, m, s, t, ss, tt; 21 int head[MAXV], nxt[MAXE<<1], eid[MAXE<<1], eidx; 22 void init( int n2, int ss2, int tt2 ) { // 初始化,设置附加源和附加汇 23 n = n2; ss = ss2; tt = tt2; 24 m = eidx = 0; 25 memset( head, -1, sizeof(head) ); 26 } 27 int adde( int u, int v, int c ) { // 添加一条只有上界的边 28 int rtn = m; 29 eid[eidx] = m; nxt[eidx] = head[u]; head[u] = eidx++; 30 edge[m++] = Edge(u,v,c,0); 31 eid[eidx] = m; nxt[eidx] = head[v]; head[v] = eidx++; 32 edge[m++] = Edge(v,u,0,0); 33 return rtn; 34 } 35 int adde2( int u, int v, int b, int c ) { // 添加一条有上下界的边,返回边的下标 36 int rtn = adde(u,v,c-b); 37 adde(ss,v,b); 38 adde(u,tt,b); 39 return rtn; 40 } 41 // 以下ISAP板子 42 int prev[MAXV], dist[MAXV], num[MAXV], cur[MAXV], res[MAXV]; 43 queue<int> bfsq; 44 void bfs() { 45 for( int i = 1; i <= n; ++i ) dist[i] = n; 46 dist[t] = 0; bfsq.push(t); 47 while( !bfsq.empty() ) { 48 int u = bfsq.front(); bfsq.pop(); 49 for( int i = head[u]; ~i; i = nxt[i] ) { 50 Edge &e = edge[eid[i]]; 51 if( dist[e.v] == n ) { 52 dist[e.v] = dist[u] + 1; 53 bfsq.push(e.v); 54 } 55 } 56 } 57 } 58 void augment() { 59 int u = t, flow = res[t]; 60 while( u != s ) { 61 int i = prev[u]; 62 edge[i].f += flow; 63 edge[i^1].f -= flow; 64 u = edge[i].u; 65 } 66 } 67 bool advance( int &u ) { 68 for( int i = cur[u]; ~i; i = nxt[i] ) { 69 Edge &e = edge[eid[i]]; 70 if( e.c > e.f && dist[e.v] + 1 == dist[u] ) { 71 prev[e.v] = cur[u] = i; 72 res[e.v] = min( res[u], e.c - e.f ); 73 u = e.v; 74 return true; 75 } 76 } 77 return false; 78 } 79 bool retreat( int &u ) { 80 if( --num[dist[u]] == 0 ) return false; 81 int newd = n; 82 for( int i = head[u]; ~i; i = nxt[i] ) { 83 Edge &e = edge[eid[i]]; 84 if( e.c > e.f ) newd = min( newd, dist[e.v] + 1 ); 85 } 86 ++num[ dist[u] = newd ]; 87 cur[u] = head[u]; 88 if( u != s ) u = edge[prev[u]].u; 89 return true; 90 } 91 int solve( int s2, int t2 ) { // 以s2为源,t2为汇跑最大流 92 s = s2; t = t2; 93 bfs(); 94 for( int i = 1; i <= n; ++i ) 95 cur[i] = head[i], ++num[dist[i]]; 96 int u = s, flow = 0; 97 res[s] = INF; 98 while( dist[s] < n ) { 99 if( u == t ) { 100 augment(); 101 flow += res[t]; 102 u = s; 103 } 104 if( !advance(u) ) 105 if( !retreat(u) ) 106 break; 107 } 108 return flow; 109 } 110 } 111 112 int n, s, t, ss, tt; // 点的个数,源,汇,附加源,附加汇 113 114 namespace Solve { 115 using ISAP::head; 116 using ISAP::nxt; 117 using ISAP::eid; 118 using ISAP::Edge; 119 using ISAP::edge; 120 bool first; 121 void dfs( int u ) { // dfs输出方案 122 // printf( "Debug: u = %d ", u ); 123 if( !first ) putchar(' '); 124 first = false; 125 printf( "%d", u ); 126 for( int i = head[u]; ~i; i = nxt[i] ) { 127 Edge &e = edge[eid[i]]; 128 if( e.v <= n && e.f > 0 ) { // 任选一条边走下去 129 // printf( "going eid = %d, from %d to %d, flow_left = %d ", eid[i], e.u, e.v, e.f ); 130 --e.f; 131 dfs(e.v); 132 return; 133 } 134 } 135 } 136 void addbound() { // 把每条边流量加上下界,恢复成原图的样子,方便输出方案 137 using ISAP::m; 138 for( int i = 0; i < m; ++i ) { 139 Edge &e = edge[eid[i]]; 140 if( e.u <= n && e.v <= n && e.c > 0 ) 141 ++e.f; 142 } 143 } 144 } 145 146 namespace Debug { // 调试用QwQ 147 void print_flow() { 148 using ISAP::edge; 149 using ISAP::Edge; 150 using ISAP::eid; 151 using ISAP::m; 152 for( int i = 0; i < m; ++i ) { 153 Edge &e = edge[eid[i]]; 154 if( e.u <= n && e.v <= n && e.c > 0 ) 155 printf( "eid = %d, from %d to %d, flow = %d ", eid[i], e.u, e.v, e.f ); 156 } 157 } 158 void print_flow2() { 159 using ISAP::edge; 160 using ISAP::Edge; 161 using ISAP::eid; 162 using ISAP::m; 163 for( int i = 0; i < m; ++i ) { 164 Edge &e = edge[eid[i]]; 165 if( e.f > 0 ) 166 printf( "eid = %d, from %d to %d, flow = %d ", eid[i], e.u, e.v, e.f ); 167 } 168 } 169 } 170 171 int main() { 172 while( scanf( "%d", &n ) == 1 ) { 173 s = n+1, t = n+2, ss = n+3, tt = n+4; 174 ISAP::init(tt,ss,tt); 175 for( int i = 1; i <= n; ++i ) { 176 int mi; scanf( "%d", &mi ); 177 while( mi-- ) { 178 int v; scanf( "%d", &v ); 179 ISAP::adde2(i,v,1,INF); 180 } 181 ISAP::adde2(s,i,0,INF); 182 ISAP::adde2(i,t,0,INF); 183 } 184 int flow1 = ISAP::solve(ss,tt); 185 // printf( "flow1 = %d ", flow1 ); 186 // Debug::print_flow(); 187 // Debug::print_flow2(); 188 int tsedge = ISAP::adde2(t,s,0,INF); // 存储弧<t,s>的信息,调试用QwQ 189 int ans = ISAP::solve(ss,tt); 190 // printf( "t_s flow = %d ", ISAP::edge[tsedge].f ); 191 // Debug::print_flow(); 192 // Debug::print_flow2(); 193 printf( "%d ", ans ); 194 Solve::addbound(); // 把每条图中的边流量加上下界,恢复成原图的样子,方便输出方案 195 while( ans ) { 196 using namespace Solve; 197 for( int i = head[s]; ~i; i = nxt[i] ) { 198 Edge &e = edge[eid[i]]; 199 if( e.v <= n && e.f > 0 ) { // 任选一个点dfs,输出方案 200 first = true; 201 --e.f; 202 --ans; 203 dfs(e.v); 204 putchar(' '); 205 } 206 } 207 } 208 } 209 return 0; 210 }