题目大意:
XWW是个影响力很大的人,他有很多的追随者。这些追随者都想要加入XWW教成为XWW的教徒。但是这并不容易,需要通过XWW的考核。
XWW给你出了这么一个难题:XWW给你一个N*N的正实数矩阵A,满足XWW性。
称一个N*N的矩阵满足XWW性当且仅当:(1)A[N][N]=0;(2)矩阵中每行的最后一个元素等于该行前N-1个数的和;(3)矩阵中每列的最后一个元素等于该列前N-1个数的和。
现在你要给A中的数进行取整操作(可以是上取整或者下取整),使得最后的A矩阵仍然满足XWW性。同时XWW还要求A中的元素之和尽量大。
思路:
首先这一题的建模还是挺有意思的。每一个点的取值有一个范围,同时还要要求它们加起来的和要在某一个范围之内。于是便自然地想到了每一个点为一条流量然后一起流到一个管道里然后这个管道的流量也是有上下界的,模型的大概就这样建出来了。
但是烦恼了我挺久的就是这题是一个二维矩阵,对于每一个点所代表的流量它既要汇合到它这一横行,又要汇合到这一竖行,显然流量是单向流动的,并且流量汇合了之后又不可以重新分开成之前没汇合的一样,这里想了我很久。
然后就突然想到,为什么一定要汇合呢?在每一行汇合前每一个格子的流量可以从它的那一列引出来啊,这样就可以当做是每一列拆开成
个流量之后再分别汇到那
行去,然后就可以完美地套用有上下界的最大流了。
有上下界的最大流大体上和有上下界的最小流是差不多的,最后的推流只不过是顺着推流罢了。
/*============================
* Author : ylsoi
* Problem : bzoj3698
* Algorithm : Max_Flow
* Time : 2018.7.29
* =========================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj3698.in","r",stdin);
freopen("bzoj3698.out","w",stdout);
}
const int maxn=100+10;
const int maxe=1e5+10;
const int inf=0x3f3f3f3f;
int n,s,t,ss,tt,sumf,ans;
int a[maxn][maxn],b[maxn][maxn],c[maxn<<1],sum;
int beg[maxn<<1],las[maxe<<1],to[maxe<<1],flow[maxe<<1],cnte=1;
void add(int u,int v,int f){
las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; flow[cnte]=f;
las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u; flow[cnte]=0;
}
struct dinic{
int num[maxn<<1],cur[maxn<<1];
queue<int>qu;
bool bfs(){
memset(num,0,sizeof(num));
num[ss]=1; qu.push(ss);
while(qu.size()){
int u=qu.front(); qu.pop();
for(int i=beg[u];i;i=las[i]){
if(!flow[i] || num[to[i]])continue;
num[to[i]]=num[u]+1;
qu.push(to[i]);
}
}
return num[tt]!=0;
}
int dfs(int u,int res){
if(u==tt || !res)return res;
int ret=0,f;
for(int &i=cur[u];i;i=las[i]){
if(num[to[i]]!=num[u]+1)continue;
if((f=dfs(to[i],min(res,flow[i])))){
ret+=f;
res-=f;
flow[i]-=f;
flow[i^1]+=f;
}
if(!res)break;
}
return ret;
}
void work(){
while(bfs()){
REP(i,ss,tt)cur[i]=beg[i];
sumf+=dfs(ss,inf);
}
}
}T;
bool init(){
scanf("%d",&n);
double tmp;
REP(i,1,n)REP(j,1,n){
scanf("%lf",&tmp);
a[i][j]=floor(tmp);
b[i][j]=ceil(tmp);
if(i==n && j==n && tmp)return false;
}
REP(i,1,n-1){
c[i]+=a[i][n];
REP(j,1,n-1)c[i]-=a[i][j];
}
REP(i,1,n-1){
c[i+n-1]-=a[n][i];
REP(j,1,n-1)c[i+n-1]+=a[j][i];
}
s=n*2-1; t=n*2;
ss=0; tt=n*2+1;
REP(i,1,n-1){
add(s,i,b[i][n]-a[i][n]);
add(i+n-1,t,b[n][i]-a[n][i]);
}
REP(i,1,n-1)REP(j,1,n-1)
add(i,j+n-1,b[i][j]-a[i][j]);
int sum1=0,sum2=0;
REP(i,1,n-1){
sum1+=a[i][n];
sum2+=a[n][i];
}
add(s,tt,sum1);
add(ss,t,sum2);
sum+=sum2;
REP(i,1,n-1){
if(c[i]>0)sum+=c[i];
if(c[i]>0)add(ss,i,c[i]);
else add(i,tt,-c[i]);
if(c[i+n-1]>0)add(ss,i+n-1,c[i+n-1]);
else add(i+n-1,tt,-c[i+n-1]);
}
add(t,s,inf);
return true;
}
int main(){
File();
if(init()){
T.work();
if(sum!=sumf){
puts("No");
return 0;
}
ans=flow[cnte];
sumf=0;
beg[ss]=0; beg[tt]=0;
beg[t]=las[beg[t]]; beg[s]=las[beg[s]];
REP(i,1,2*n)beg[i]=las[beg[i]];
add(ss,s,inf); add(t,tt,inf);
T.work();
printf("%d\n",(ans+sumf)*3);
}
else puts("No");
return 0;
}