【题目描述】
Description
DZY家的后院有一块地,由N行M列的方格组成,格子内种的菜有一定的价值,并且每一条单位长度的格线有一定的费用。
DZY喜欢在地里散步。他总是从任意一个格点出发,沿着格线行走直到回到出发点,且在行走途中不允许与已走过的路线有任何相交或触碰(出发点除外)。记这条封闭路线内部的格子总价值为V,路线上的费用总和为C,DZY想知道V/C的最大值是多少。
Input
第一行为两个正整数n,m。
接下来n行,每行m个非负整数,表示对应格子的价值。
接下来n+1行,每行m个正整数,表示所有横向的格线上的费用。
接下来n行,每行m+1个正整数,表示所有纵向的格线上的费用。
(所有数据均按从左到右,从上到下的顺序输入,参见样例和配图)
Output
输出一行仅含一个数,表示最大的V/C,保留3位小数。
Sample Input
3 4
1 3 3 3
1 3 1 1
3 3 1 0
100 1 1 1
97 96 1 1
1 93 92 92
1 1 90 90
98 1 99 99 1
95 1 1 1 94
1 91 1 1 89
1 3 3 3
1 3 1 1
3 3 1 0
100 1 1 1
97 96 1 1
1 93 92 92
1 1 90 90
98 1 99 99 1
95 1 1 1 94
1 91 1 1 89
Sample Output
1.286
HINT
Source
【题解】 看到求比值就知道这题一定要0/1分数规划,关键是怎么判断答案是否可行。
题目可以转换为最大全闭合子图的模型,
原点S 往每个点连边,流量为这个点的权值
相邻的点互相连边,流量为这两个点夹着的割线的费用。
在最外圈的点往T连边,流量为这个点所靠边界的费用。
当前答案=权值和-最大流
直观感受,割掉与S的边相当于不选这个点,割掉相邻个点的边相当于一个选一个不选,割掉连向T的边说明选这个边界上的点。
/* --------------
user Vanisher
problem bzoj-3232
----------------*/
# include <bits/stdc++.h>
# define ll long long
# define inf 1e9
# define eps 1e-5
# define N 110
# define M 1000100
using namespace std;
int read(){
int tmp=0, fh=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
return tmp*fh;
}
struct node{
int data,next,re;
double l;
}e[M];
int head[N*N],place,S,T,dis[N*N],q[N*N],now[N*N],n,m,p[N][N],w[N][N],sum,cx[N][N],cy[N][N];
void build(int u, int v, double l){
e[++place].data=v; e[place].next=head[u]; head[u]=place; e[place].l=l; e[place].re=place+1;
e[++place].data=u; e[place].next=head[v]; head[v]=place; e[place].l=0; e[place].re=place-1;
}
void bfs(){
for (int i=1; i<=T; i++) dis[i]=inf;
int pl=1, pr=1; q[1]=S;
while (pl<=pr){
int x=q[pl++];
for (int ed=head[x]; ed!=0; ed=e[ed].next)
if (dis[e[ed].data]==inf&&e[ed].l>=eps)
dis[e[ed].data]=dis[x]+1, q[++pr]=e[ed].data;
}
}
double dfs(int x, double flow){
if (x==T) return flow;
double sum=0;
for (int ed=now[x]; ed!=0; ed=e[ed].next)
if (e[ed].l>=eps&&dis[e[ed].data]==dis[x]+1){
double tmp=dfs(e[ed].data,min(flow,e[ed].l));
sum=sum+tmp; flow=flow-tmp;
e[ed].l-=tmp; e[e[ed].re].l+=tmp;
if (flow<eps){
now[x]=ed;
return sum;
}
}
now[x]=0;
return sum;
}
double dinic(){
double sum=0;
for (bfs(); dis[T]!=inf; bfs()){
for (int i=S; i<=T; i++) now[i]=head[i];
sum=sum+dfs(S,inf);
}
return sum;
}
double check(double d){
place=0; memset(head,0,sizeof(head));
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
build(S,p[i][j],w[i][j]);
for (int i=1; i<=n-1; i++)
for (int j=1; j<=m; j++)
build(p[i][j],p[i+1][j],cx[i+1][j]*d);
for (int i=2; i<=n; i++)
for (int j=1; j<=m; j++)
build(p[i][j],p[i-1][j],cx[i][j]*d);
for (int i=1; i<=n; i++)
for (int j=1; j<=m-1; j++)
build(p[i][j],p[i][j+1],cy[i][j+1]*d);
for (int i=1; i<=n; i++)
for (int j=2; j<=m; j++)
build(p[i][j],p[i][j-1],cy[i][j]*d);
for (int i=1; i<=n; i++){
build(p[i][1],T,cy[i][1]*d);
build(p[i][m],T,cy[i][m+1]*d);
}
for (int i=1; i<=m; i++){
build(p[1][i],T,cx[1][i]*d);
build(p[n][i],T,cx[n+1][i]*d);
}
return sum-dinic();
}
int main(){
n=read(), m=read(); S=0;
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++){
w[i][j]=read(), sum=sum+w[i][j];
p[i][j]=++place;
}
T=++place;
for (int i=1; i<=n+1; i++)
for (int j=1; j<=m; j++)
cx[i][j]=read();
for (int i=1; i<=n; i++)
for (int j=1; j<=m+1; j++)
cy[i][j]=read();
double pl=0, pr=10000.00, ans=0;
while (pl<=pr){
double mid=(pl+pr)/2;
if (check(mid)>eps)
ans=mid, pl=mid+eps;
else pr=mid-eps;
}
printf("%.3lf",ans);
return 0;
}