bzoj 3232: 圈地游戏【分数规划+最小割】

数组开小导致TTTTTLE……
是分数规划,设sm为所有格子价值和,二分出mid之后,用最小割来判断,也就是判断sm-dinic()>=0
这个最小割比较像最大权闭合子图,建图是s像所有点连流量为格子价值的边(相当于最大权闭合子图中的正权点),然后考虑边缘,两个相邻的格子,如果一个选一个不选那么中间这条边就有负的贡献,所以两个相邻的格子之间连两条边权为mid*边权的边,注意是两条,要互相连一下,然后所有边界上的点像t连边权为mid*边界边权的边,相当于假装外面还有一层点全标为t,然后跑最小割判断即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=105;
const double eps=1e-6,inf=1e9;
int n,m,h[N*N],cnt,s,t,id[N][N],tot,le[N*N];
double a[N][N],b[N][N],c[N][N],sm;
struct qwe
{
	int ne,to;
	double va;
}e[N*N*N];
void add(int u,int v,double w)
{
	cnt++;
	e[cnt].ne=h[u];
	e[cnt].to=v;
	e[cnt].va=w;
	h[u]=cnt;
}
void ins(int u,int v,double w)
{
	add(u,v,w);
	add(v,u,0);
}
bool bfs()
{
	queue<int>q;
	memset(le,0,sizeof(le));
	le[s]=1;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=h[u];i;i=e[i].ne)
			if(e[i].va>eps&&!le[e[i].to])
			{
				le[e[i].to]=le[u]+1;
				q.push(e[i].to);
			}
	}
	return le[t];
}
double dfs(int u,double f)
{
	if(u==t||!f)
		return f;
	double us=0;
	for(int i=h[u];i&&us<f;i=e[i].ne)
		if(e[i].va>eps&&le[e[i].to]==le[u]+1)
		{
			double t=dfs(e[i].to,min(e[i].va,f-us));
			e[i].va-=t;
			e[i^1].va+=t;
			us+=t;
		}
	if(us<eps)
		le[u]=0;
	return us;
}
int dinic()
{
	double re=0;
	while(bfs())
		re+=dfs(s,inf);
	return re;
}
bool ok(double w)
{
	memset(h,0,sizeof(h));
	cnt=1,s=0,t=n*m+1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			ins(s,id[i][j],a[i][j]);
	for(int j=1;j<=m;j++)
		ins(id[1][j],t,w*b[0][j]),ins(id[n][j],t,w*b[n][j]);
	for(int i=1;i<=n;i++)
		ins(id[i][1],t,w*c[i][0]),ins(id[i][m],t,w*c[i][m]);
	for(int i=1;i<n;i++)
		for(int j=1;j<=m;j++)
			add(id[i][j],id[i+1][j],w*b[i][j]),add(id[i+1][j],id[i][j],w*b[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<m;j++)
			add(id[i][j],id[i][j+1],w*c[i][j]),add(id[i][j+1],id[i][j],w*c[i][j]);
	return sm-dinic()>eps;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%lf",&a[i][j]),id[i][j]=++tot,sm+=a[i][j];
	for(int i=0;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%lf",&b[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=0;j<=m;j++)
			scanf("%lf",&c[i][j]);
	double l=0,r=n*m*100,ans=0;
	while(r-l>1e-5)
	{
		double mid=(l+r)/2;
		if(ok(mid))
			l=mid,ans=mid;
		else
			r=mid;
	}
	printf("%.3f\n",ans);
	return 0;
}
posted @ 2018-10-16 07:16  lokiii  阅读(166)  评论(0编辑  收藏  举报