题目描述:
给定一个n*m(n.m<=200)的矩阵A,最小化:
Max{
max {|∑(A ij − B ij )|} 1≤j≤ m
max {|∑(A ij − B ij ) |} 1≤i≤n
<1>题目分析
这题的形式十分像线性规划问题,因此可以考虑用网络流。
网络流处理矩阵类问题的一种经典做法是——开一排表示行的节点,开一排表示列的节点,然后balabala……
于是这题的模型大概就出来了……
<2>有上下界网络流问题的模型构建
基本思想是考虑自由流——即除去满足下界的流。
先考虑无源无汇的情况:
新建源S汇T。对于每个节点d,设其入弧下界和为in,出弧下界和为out:
1.若in>out则连S->d,容量为delta。
2.若in<out则连d->T,容量为delta。
3.将每条原弧容量改成上界减下界。
(上述建图的基本思想是保证自由流中的流量平衡
从S到T跑一遍最大流后,如果每条和S或T相连的边均满流,则有可行流,否则不存在可行流。
如果有源有汇呢?直接从汇到源连一条容量为无穷的边就转化成无源无汇了。
Ex:
可行最大流——改回原图,从S到T增广自由流。
可行最小流——改回原图,从T到S增广自由流。
<3>算法描述
对于本题,可以用最大值最小的一般方法——二分枚举答案ans,那么对每一行每一列的总和就有要求的最小和最大值了。
于是流量表示数值,稍微YY下图就构出来了。
注意区分原来的s t和新建的S T……
Code:
#include<cstdio>
#include<cstring>
using namespace std;
const int SN=500, SM=100010, oo=999999999;
#define max(_,__) ({ int ___=_,____=__; (___>____)?___:____; })
#define min(_,__) ({ int ___=_,____=__; (___<____)?___:____; })
int n,m,A[300][300],ht[300],lt[300],B[300][300];
int lans,rans,md,L,R,s,t,S,T;
int tot[SN],la[SN],fr[SM],to[SM],rs[SM],nx[SM],bn;
int ln[SN],l,r,hei[SN],Bt,vis[SN];
void link(int u, int v, int w)
{
bn++, fr[bn]=u, to[bn]=v, rs[bn]=w, nx[bn]=la[u], la[u]=bn;
bn++, fr[bn]=v, to[bn]=u, rs[bn]=0, nx[bn]=la[v], la[v]=bn;
}
void clean()
{
Bt=0, bn=1;
memset(tot,0,sizeof(tot));
memset(vis,0,sizeof(vis));
memset(la ,0,sizeof(la ));
}
bool BFS()
{
int j;
for(ln[l=r=1]=T,hei[T]=1,vis[T]=++Bt; l<=r; l++)
for(j=la[ln[l]]; j; j=nx[j])
if(rs[j^1] && vis[to[j]]!=Bt)
{
ln[++r]=to[j], vis[to[j]]=Bt;
hei[to[j]]=hei[ln[l]]+1;
if(to[j]==S) return true;
}
return false;
}
int DFS(int d, int p)
{
int j,flow=0,f;
if(d==T || p==0) return p;
for(j=la[d]; j; j=nx[j])
if(vis[to[j]]==Bt && hei[to[j]]==hei[d]-1)
if( rs[j] && ( f=DFS(to[j], min(p,rs[j])) )>0 )
{
rs[j]-=f, rs[j^1]+=f;
flow+=f, p-=f;
if(!p) return flow;
}
hei[d]=oo;
return flow;
}
bool canflow()
{
while(BFS())
DFS(S,oo);
for(int j=2; j<=bn; j++)
if( (fr[j]==S || to[j]==T) && j<(j^1) && rs[j] ) return false;
return true;
}
bool solve()
{
int i,j,dw,up;
clean();
s=n+m+1,t=s+1; S=t+1,T=S+1;
for(i=1; i<=n; i++) dw=max(ht[i]-md,0), up=ht[i]+md, tot[ i ]+=dw, link( s , i, up-dw), tot[s]-=dw;
for(i=1; i<=m; i++) dw=max(lt[i]-md,0), up=lt[i]+md, tot[i+n]-=dw, link(i+n, t, up-dw), tot[t]+=dw;
for(i=1; i<=n; i++)
for(j=1; j<=m; j++)
tot[i]-=L, tot[j+n]+=L, link(i, j+n, R-L);
link(t, s, oo);
for(i=1; i<=t; i++)
{
if(tot[i]>0) link(S, i, tot[i]);
if(tot[i]<0) link(i, T, -tot[i]);
}
return canflow();
}
int main()
{
int i,j;
freopen("mat.in" , "r", stdin);
freopen("mat.out", "w", stdout);
scanf("%d%d", &n, &m);
for(i=1; i<=n; i++)
for(j=1; j<=m; j++)
{
scanf("%d", &A[i][j]);
ht[i] += A[i][j];
lt[j] += A[i][j];
}
scanf("%d%d", &L, &R);
for(lans=0,rans=200000,md=(lans+rans)/2; lans<rans; md=(lans+rans)/2)
if(solve()) rans=md;
else lans=md+1;
md=lans; solve();
printf("%d\n", md);
for(i=2; i<=bn; i++)
if(fr[i]>=1 && fr[i]<=n && i<(i^1))
B[fr[i]][to[i]-n] = R-rs[i];
for(i=1; i<=n; i++)
{
for(j=1; j<=m; j++) printf("%d ", B[i][j]);
printf("\n");
}
return 0;
}