【BZOJ 3232】圈地游戏 分数规划+最大权闭合图

题解:首先肯定是分数规划这没问题,然后在于怎么建边,怎么确定二分方向,即“check”。

建图:好吧其实我也没弄懂建图到底怎么回事,自责一下。但是依然要贴一下代码,如果不求甚解的话这么建图代码肯定是又快又短的,当然,如果谁弄懂了给个回复大笑

codes:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 500
#define NN 50000
#define M 301000
#define inf 200000000.0
#define eps 1e-5
using namespace std;
struct KSD
{
	int u,v,next;
	double len;
}e[M];
int head[NN],id[N][N],d[NN],n,m,s,t,cnt;
double cross[N][N],stand[N][N],w[N][N],sum,maxflow;
void add(int u,int v,double len)
{
	cnt++;
	e[cnt].u=u;
	e[cnt].v=v;
	e[cnt].len=len;
	e[cnt].next=head[u];
	head[u]=cnt;
}
void edit(double mid)
{
	int i,j;
	cnt=1;maxflow=0;
	memset(head,0,sizeof(head));
	memset(e,0,sizeof(e));
	for(i=0;i<=n+1;i++)for(j=0;j<=m+1;j++)
	{
		if(i<1||i>n||j<1||j>m)
		{
			add(id[i][j],t,inf);
			add(t,id[i][j],0);
		}
		else
		{
			add(s,id[i][j],w[i][j]);
			add(id[i][j],s,0);
		}
	}
	for(i=0;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			add(id[i][j],id[i+1][j],mid*cross[i][j]);
			add(id[i+1][j],id[i][j],mid*cross[i][j]);
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=0;j<=m;j++)
		{
			add(id[i][j],id[i][j+1],mid*stand[i][j]);
			add(id[i][j+1],id[i][j],mid*stand[i][j]);
		}
	}
}
void build()
{
	int i,j;
	cnt=0;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			scanf("%lf",&w[i][j]);
			sum+=w[i][j];
		}
	}
	for(i=0;i<=n+1;i++)
	{
		for(j=0;j<=m+1;j++)
		{
			id[i][j]=++cnt;
		}
	}
	for(i=0;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			scanf("%lf",&cross[i][j]);
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=0;j<=m;j++)
		{
			scanf("%lf",&stand[i][j]);
		}
	}
	s=0,t=cnt+1;
}
int bfs()
{
	int i,u,v;
	memset(d,0,sizeof(d));
	queue<int>q;
	q.push(s);
	d[s]=1;
	while(!q.empty())
	{
		u=q.front();
		q.pop();
		for(i=head[u];i;i=e[i].next)
		{
			v=e[i].v;
			if(d[v]||e[i].len<eps)continue;
			d[v]=d[u]+1;
			if(v==t)return 1;
			q.push(v);
		}
	}
	return 0;
}
double dinic(int x,double f)
{
	if(x==t)return f;
	int i,v;
	double remain=f,k;
	for(i=head[x];i&&remain>=eps;i=e[i].next)
	{
		v=e[i].v;
		if(e[i].len>=eps&&d[v]==d[x]+1)
		{
			k=dinic(v,min(remain,e[i].len));
			if(k<eps)d[v]=0;
			else
			{
				e[i].len-=k;
				e[i^1].len+=k;
				remain-=k;
			}	
		}			
	}
	return f-remain;
}
int main()
{
//	freopen("test.in","r",stdin);
	int i;
	double l,r,mid;
	build();
	l=0,r=n*m*100.0,mid=n*m*50.0;
	for(i=1;i<=35;i++,mid=(l+r)/2.0)
	{
		edit(mid);
		while(bfs())maxflow+=dinic(s,inf);
		if (sum-maxflow<=1e-9)r=mid;
		else l=mid;
	}
	printf("%.3lf",l);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值