[bzoj3232]圈地游戏【0/1分数规划】【网络流】

【题目描述】

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

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;
}


posted @ 2018-01-27 11:05  Vanisher  阅读(117)  评论(0编辑  收藏  举报