欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

[Wf2011]Chips Challenge(最小费用最大流)

发布时间:2023/12/3 编程问答 42 豆豆
生活随笔 收集整理的这篇文章主要介绍了 [Wf2011]Chips Challenge(最小费用最大流) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

[Wf2011]Chips Challenge

  • problem
  • solution
  • code

problem

BZOJ2673

solution

.

首先得知道这是网络流,但真的看不出来啊!!我真的郁闷啊( ̄﹏ ̄;)

在知道做法是网络流后,初读题,肯定会想到将行列分左右点,[1,n][1,n][1,n]表示行,[n+1,2n][n+1,2n][n+1,2n]表示列。

然后就发现举步维艰,因为题目要维护的两个条件都不是很好操作。

iiiiii 列零件数一样,且每行零件数不能超过总数的 AB\frac{A}{B}BA

这两个限制条件都没有明确具体零件个数要求,是随着全局动态变化的。

所以无法变成网络流上的容量限制。

唯一知道的限制就是每行每列可放芯片的最大数量。

【可放指的是 . /C 的点】

注意到 nnn 的限制很小,不妨考虑枚举每行可放零件的最大值个数 MaxMaxMax

如果能通过网络流求出总零件数量 tottottot,就可以逆向判断 Max≤ABtot⇒Max∗B≤A∗totMax\le \frac{A}{B}tot\Rightarrow Max*B\le A*totMaxBAtotMaxBAtot

而网络流是最大流,是尽量做到流满的,所以 tottottot 一定会尽可能的大。

增长同向平行,最大流就是在做尽量满足判断式子的过程。

所以如果最大流的结果还是不满足上面的不等式,只能说明 MaxMaxMax 不是合法的。

自然地想到,对于可放芯片的位置 (i,j)(i,j)(i,j) 进行 iii 行向 jjj 列连边,容量为 111

这样去跑网络流,跑出的最大流确实是最多能放的芯片数量,但是这样并没有考虑到 iiiiii 列相等的限制。

因为不能知道第 iii 行和第 iii 列具体放了多少个芯片,没有明确的容量限制。

但是转化一下,第 iii 行放了芯片的位置和第 iii 列没放芯片的位置加起来一共是等于 nnn 的。

所以想到新建一个连接点 kkk,分别让行列点向 kkk 连边。

行连接的边流过的流量来表示行放芯片的个数,列连接的边流过的流量来表示列不放芯片的个数。

行列与 kkk 之间的边容量设为 ∞∞

然后连接点向 ttt 连边,容量为 nnn,保证 k→tk\rightarrow tkt 的边满流即可。

因为这样代表着第 iii 行放的个数和第 iii 列不放的个数加在一起是等于 nnn 的,变相地满足第 iii 行和第 iii 列个数相同的限制。

但是怎么能一些边流了表示不选,一些边流了表示要选。最大流上面看到了是不能做到的,那就只能考虑最小费用了。

iii 流给连接点 kkk 的表示要放的芯片,列 iii 流给 kkk 的表示不放的芯片,所以 i→ji\rightarrow jij 这种流给其它列点就应当表示 (i,j)(i,j)(i,j) 不放芯片,所以只有 iiijjj 列是 . 才有选择不放的可能。

为了记录这样的边流了多少,就把这种边带上费用 111,跑最小费用就是尽可能减少不放的,即最大化放的芯片。

除了这样的边,其余边费用就为 000,属于要跑最大流。

别忘了,一开始枚举的每行放芯片个数的限制,所以行与连接点之间的容量应该修改为 MaxMaxMax

注意到,现在的网络还有一个冗余的地方,列 iii 的出边只有连接点 kkk,且容量为 ∞∞,所以可以将列 iii 与连接点进行合并。

那么现在的网络,行 iii 的流量只有两种去向:经过列 iii 最多流 MaxMaxMax 个,剩下的都是流到其他列 jjj 地方,表示不放芯片,且带有费用 111

最后最大流减去最小费用就是真正放的芯片个数,再减去一定放的 C 个数就是新放的芯片个数。

最后记得还有行列分别与源汇的连边。

通过 s→is\rightarrow isi 容量为第 iii 行可放的芯片数量来限制第 iii 行放置的个数。

通过 j→tj\rightarrow tjt 容量为第 jjj 列可放的芯片数量来限制第 jjj 列放置的个数。【准确来说是 j+n→tj+n\rightarrow tj+nt,大家意会就行】

最后让这个网络满流即可。

总结一下建图过程:
{s→irowi,0i+n→ncoli,0i→j+n1,1(ch[i][j]=′.′)i→i+nMax,0\begin{cases}s\rightarrow i\quad \quad \quad row_i,0\\i+n\rightarrow n\quad col_i,0\\i\rightarrow j+n\quad 1,1\ (ch[i][j]='.')\\i\rightarrow i+n\quad Max,0\end{cases} sirowi,0i+nncoli,0ij+n1,1 (ch[i][j]=.)ii+nMax,0

code

#include <bits/stdc++.h> using namespace std; #define maxn 105 #define maxm 5005 int n, a, b, s, t, cnt; bool vis[maxn]; char ch[maxn][maxn]; int dis[maxn], head[maxn], lst[maxn], row[maxn], col[maxn]; struct node { int to, nxt, flow, w; }E[maxm]; queue < int > q;void addedge( int u, int v, int flow, int w ) {E[cnt] = { v, head[u], flow, w };head[u] = cnt ++;E[cnt] = { u, head[v], 0, -w };head[v] = cnt ++; }bool spfa() {memset( dis, 0x7f, sizeof( dis ) );memset( lst, -1, sizeof( lst ) );dis[s] = 0; q.push( s );while( ! q.empty() ) {int u = q.front(); q.pop(); vis[u] = 0;for( int i = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( dis[v] > dis[u] + E[i].w and E[i].flow ) {dis[v] = dis[u] + E[i].w;lst[v] = i;if( ! vis[v] ) vis[v] = 1, q.push( v );}}}return ~ lst[t]; }void MCMF( int &flow, int &cost ) {flow = cost = 0;while( spfa() ) {int Min = 1e9;for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] )Min = min( Min, E[i].flow );for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] )cost += E[i].w * Min, E[i].flow -= Min, E[i ^ 1].flow += Min;flow += Min;} }int main() {int Case = 0;while( ~ scanf( "%d %d %d", &n, &a, &b ) ) {if( ! n and ! a and ! b ) break;memset( row, 0, sizeof( row ) );memset( col, 0, sizeof( col ) );s = 0, t = n << 1 | 1;int sum = 0, used = 0;for( int i = 1;i <= n;i ++ ) {scanf( "%s", ch[i] + 1 );for( int j = 1;j <= n;j ++ )if( ch[i][j] != '/' ) {sum ++, row[i] ++, col[j] ++;used += ch[i][j] == 'C';}}int ans = -1;for( int Max = 0, flow, cost;Max <= n;Max ++ ) {memset( head, -1, sizeof( head ) ); cnt = 0;for( int i = 1;i <= n;i ++ ) {addedge( s, i, row[i], 0 );addedge( i + n, t, col[i], 0 );addedge( i, i + n, Max, 0 );for( int j = 1;j <= n;j ++ )if( ch[i][j] == '.' ) addedge( i, j + n, 1, 1 );}MCMF( flow, cost );if( flow == sum and Max * b <= ( sum - cost ) * a )ans = max( ans, sum - cost );}printf( "Case %d: ", ++ Case );if( ~ ans ) printf( "%d\n", ans - used );else printf( "impossible\n" );}return 0; }

总结

以上是生活随笔为你收集整理的[Wf2011]Chips Challenge(最小费用最大流)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。