[BZOJ3232]圈地游戏

bzoj

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

sol

肯定是先分数规划一波啦。
这样一来选出的点产生正权,选出的边产生负权,可以跑一个最大权闭合子图判断是否存在一种选法使权值和大于零。
具体的建图:

1、源点\(S\)向每个点连点权的边,表示若不选这个点需要付出点权的代价。
2、相邻点之间连\(mid*\)边权的双向边,表示若两个点的选取状态不同,则它们中间的那条边就一定被经过了,需要付出\(mid*\)边权的代价。
3、边界上的点向汇点\(T\)\(mid*\)边权的边,表示这个点若要选就需要走过边界上的这条边。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
#define P(i,j) ((i-1)*m+j)
const int N = 55;
const double eps = 1e-5;
struct edge{int to,nxt;double w;}a[N*N*N];
int n,m,S,T,head[N*N],cnt=1,dep[N*N],cur[N*N];
double sum,val[N][N],h[N][N],z[N][N];
queue<int>Q;
void link(int u,int v,double w){
	a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
	a[++cnt]=(edge){u,head[v],0};head[v]=cnt;
}
bool bfs(){
	memset(dep,0,sizeof(dep));
	dep[S]=1;Q.push(S);
	while (!Q.empty()){
		int u=Q.front();Q.pop();
		for (int e=head[u];e;e=a[e].nxt)
			if (a[e].w>eps&&!dep[a[e].to])
				dep[a[e].to]=dep[u]+1,Q.push(a[e].to);
	}
	return dep[T];
}
double dfs(int u,double f){
	if (u==T) return f;
	for (int &e=cur[u];e;e=a[e].nxt)
		if (a[e].w>eps&&dep[a[e].to]==dep[u]+1){
			double tmp=dfs(a[e].to,min(a[e].w,f));
			if (tmp>eps) {a[e].w-=tmp;a[e^1].w+=tmp;return tmp;}
		}
	return 0;
}
double dinic(){
	double res=0;
	while (bfs()){
		for (int i=1;i<=T;++i) cur[i]=head[i];
		while (233){
			double tmp=dfs(S,1e9);
			if (tmp<=eps) break;res+=tmp;
		}
	}
	return res;
}
bool check(double mid){
	memset(head,0,sizeof(head));cnt=1;
	for (int i=1;i<=n;++i)
		for (int j=1;j<=m;++j)
			link(S,P(i,j),val[i][j]);
	for (int i=1;i<n;++i)
		for (int j=1;j<=m;++j)
			link(P(i,j),P(i+1,j),mid*h[i][j]),link(P(i+1,j),P(i,j),mid*h[i][j]);
	for (int i=1;i<=n;++i)
		for (int j=1;j<m;++j)
			link(P(i,j),P(i,j+1),mid*z[i][j]),link(P(i,j+1),P(i,j),mid*z[i][j]);
	for (int i=1;i<=m;++i) link(P(1,i),T,mid*h[0][i]),link(P(n,i),T,mid*h[n][i]);
	for (int i=1;i<=n;++i) link(P(i,1),T,mid*z[i][0]),link(P(i,m),T,mid*z[i][m]);
	return sum-dinic()>eps;
}
int main(){
	n=gi();m=gi();S=n*m+1;T=S+1;
	for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) val[i][j]=gi(),sum+=val[i][j];
	for (int i=0;i<=n;++i) for (int j=1;j<=m;++j) h[i][j]=gi();
	for (int i=1;i<=n;++i) for (int j=0;j<=m;++j) z[i][j]=gi();
	double l=0,r=1e9;
	while (r-l>eps){
		double mid=(l+r)/2;
		if (check(mid)) l=mid;else r=mid;
	}
	printf("%.3lf\n",l);return 0;
}
posted @ 2018-05-24 14:29  租酥雨  阅读(193)  评论(0编辑  收藏  举报