最小矩阵差——有上下界的网络流问题

题目描述:

给定一个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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值