【A* 网络流】codechef Chef and Cut
高嘉煊讲的杂题;A*和网络流的练手题
题目大意
https://s3.amazonaws.com/codechef_shared/download/translated/SEPT16/mandarin/CHEFKC.pdf
一张有向带权图,固定$S$和$T$,求权值第$k$小的割。
$n \le 77,m\le 777,k \le 777$
题目分析
考虑如何描述每一种割:$p_i=1$表示$S$与$i$连通;$p_i=0$表示$i$与$T$连通。注意到这里的$\{p_i\}$与每种割是一一对应的,那么就可以以$p_i$为状态进行A*扩展求第$k$大状态。
接下去的问题就是如何计算$\{p_i\}$的权值。那么对于$p_i=1$,连边$(S,i,INF)$表示$(S,i)$无法割去;$p_i=0$同理。对于这张图做最小流即可。
最后可能是一个A*不同写法上的技巧。讲课时候讲的是一个状态$(len,\{p_i\},val)$表示只确认前$len$位的状态$p_i$的权值$val$。这种扩展是每次一步步确认元素,时空效率都不高。另一种写法直接$(len,\{p_i\},val)$表示$n$个点的状态为$\{p_i\}$,确定不改变 前$len$个元素,这个情况下的权值$val$。每次转移的时候,枚举新状态确认的长度$len'=len+1\cdots n$,保留前$len'-1$个状态并将$len'$位取反(这个处理是为了保证不重不漏遍历所有状态),然后以此状态处理出的最小割作为新的状态,这样就能保证省略很多中途步骤的无用状态而直接取当前状态的最优方案(因为既然要按顺序,那么必定从每个子状态的最优状态再进一步考虑)。
反正效率还不错,cc榜rk4.
1A还行
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const int maxn = 103; 4 const int maxm = 5035; 5 const int INF = 1e9; 6 7 struct Edge 8 { 9 int u,v,f,c; 10 Edge(int a=0, int b=0, int c=0, int d=0):u(a),v(b),f(c),c(d) {} 11 }edges[maxm],sv[maxm]; 12 struct node 13 { 14 int len,val; 15 bool a[maxn]; 16 bool operator < (node a) const 17 { 18 return val > a.val; 19 } 20 void init(){memset(a, 0, sizeof a);} 21 }tmp,trans; 22 bool vis[maxn]; 23 int n,m,k,S,T,sta,end; 24 int edgeTot,head[maxn],nxt[maxm],lv[maxn]; 25 std::priority_queue<node> q; 26 27 void addedge(int u, int v, int c) 28 { 29 edges[edgeTot] = Edge(u, v, 0, c), nxt[edgeTot] = head[u], head[u] = edgeTot, ++edgeTot; 30 edges[edgeTot] = Edge(v, u, 0, 0), nxt[edgeTot] = head[v], head[v] = edgeTot, ++edgeTot; 31 } 32 bool buildLevel() 33 { 34 std::queue<int> q; 35 memset(lv, 0, sizeof lv); 36 q.push(S), lv[S] = 1; 37 for (int tmp; q.size(); ) 38 { 39 tmp = q.front(), q.pop(); 40 for (int i=head[tmp]; i!=-1; i=nxt[i]) 41 { 42 int v = edges[i].v; 43 if (edges[i].f < edges[i].c&&!lv[v]){ 44 lv[v] = lv[tmp]+1, q.push(v); 45 if (v==T) return true; 46 } 47 } 48 } 49 return false; 50 } 51 int fndPath(int x, int lim) 52 { 53 if (!lim||x==T) return lim; 54 int sum = 0; 55 for (int i=head[x]; i!=-1&&sum < lim; i=nxt[i]) 56 { 57 int v = edges[i].v, val = 0; 58 if (lv[v]==lv[x]+1&&edges[i].f < edges[i].c){ 59 if ((val = fndPath(v, std::min(edges[i].c-edges[i].f, lim-sum)))){ 60 sum += val, edges[i].f += val, edges[i^1].f -= val; 61 }else lv[v] = -1; 62 } 63 } 64 return sum; 65 } 66 int dinic() 67 { 68 int ret = 0, val = 0; 69 while (buildLevel()) 70 while ((val = fndPath(S, INF))) ret += val; 71 return ret; 72 } 73 void calc(node &x) 74 { 75 memset(head, -1, sizeof head), edgeTot = 0; 76 for (int i=1; i<=m; i++) addedge(sv[i].u, sv[i].v, sv[i].c); 77 addedge(S, sta, INF), addedge(end, T, INF); 78 for (int i=1; i<=x.len; i++) 79 if (x.a[i]) addedge(S, i, INF); 80 else addedge(i, T, INF); 81 std::queue<int> q; 82 x.val = dinic(), q.push(S); 83 memset(vis, 0, sizeof vis); 84 for (int i=1; i<=n; i++) x.a[i] = 0; 85 for (int tmp; q.size(); ) 86 { 87 tmp = q.front(), q.pop(); 88 for (int i=head[tmp]; i!=-1; i=nxt[i]) 89 if (edges[i].f < edges[i].c){ 90 int v = edges[i].v; 91 if (!vis[v]) vis[v] = true, x.a[v] = 1, q.push(v); 92 } 93 } 94 } 95 int main() 96 { 97 freopen("CHEFKC.in","r",stdin); 98 freopen("CHEFKC.out","w",stdout); 99 scanf("%d%d%d%d%d",&n,&m,&k,&sta,&end), S = 0, T = n+1; 100 for (int i=1; i<=m; i++) scanf("%d%d%d",&sv[i].u,&sv[i].v,&sv[i].c); 101 tmp.len = 0, tmp.a[sta] = true, calc(tmp), q.push(tmp); 102 while (--k) 103 { 104 tmp = q.top(), q.pop(); 105 for (int i=tmp.len+1; i<=n; i++) 106 { 107 trans.init(), trans.len = i, trans.a[i] = !tmp.a[i]; 108 for (int j=1; j<i; j++) trans.a[j] = tmp.a[j]; 109 calc(trans), q.push(trans); 110 } 111 } 112 printf("%d\n",q.top().val); 113 return 0; 114 }
END