题意:
在一个n*m的网格里,边上有花费,格里有权值;
从任意一个点开始绕一圈,绕一个简单环出来,里面的所有格子就是收益;
求最大的收益/花费;
所有数<=100;
题解:
考虑01分数规划的方式,但是花费和权值不在一起;
那么考虑将格内的权值转化到边上;
实际上将边有向化,按边方向左面一行的权值为正,右面为负,加起来作为选了这条边的权值;
这样在围成一个环的过程中会将外面的点消掉,而里面的点是四倍大小;
问题转化成二分答案,对一个图进行check,考虑其中有没有正权的环;
上一个BF判断就好了;
时间复杂度O(n^2*m^2*log400);
二分上界定为400就可以了,别忘了答案要除4;
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110
using namespace std;
int to[N*N<<2],next[N*N<<2],val[N*N<<2],cost[N*N<<2],head[N*N],tot;
int n,m,L[N][N],U[N][N],num[N][N];
double dis[N*N];
void add(int x,int y,int v,int c)
{
to[++tot]=y,val[tot]=v,cost[tot]=c,next[tot]=head[x],head[x]=tot;
}
bool check(double L)
{
memset(dis,0,sizeof(dis));
int i,j,k,x,y;
for(k=1;k<=(n+1)*(m+1);k++)
{
bool flag=0;
for(x=1;x<=(n+1)*(m+1);x++)
{
for(i=head[x];i;i=next[i])
{
y=to[i];
if(dis[y]>dis[x]+L*cost[i]-val[i])
dis[y]=dis[x]+L*cost[i]-val[i],flag=1;
}
}
if(!flag)
return 0;
}
return 1;
}
int main()
{
int i,j,k,x;
scanf("%d%d",&n,&m);
for(i=0,k=0;i<=n;i++)
for(j=0;j<=m;j++)
num[i][j]=++k;
for(i=1,k=0;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%d",&x);
L[i][j]=L[i][j-1]+x;
U[i][j]=U[i-1][j]+x;
}
}
for(i=0;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%d",&x);
add(num[i][j-1],num[i][j],(U[i][j]<<1)-U[n][j],x);
add(num[i][j],num[i][j-1],U[n][j]-(U[i][j]<<1),x);
}
}
for(i=1;i<=n;i++)
{
for(j=0;j<=m;j++)
{
scanf("%d",&x);
add(num[i-1][j],num[i][j],L[i][m]-(L[i][j]<<1),x);
add(num[i][j],num[i-1][j],(L[i][j]<<1)-L[i][m],x);
}
}
double l=0,r=400,mid;
while(r-l>1e-5)
{
mid=(l+r)/2;
if(check(mid))
l=mid;
else
r=mid;
}
printf("%.3lf",l/4);
return 0;
}