01分数规划
简化:即对于 ∑ a [ i ] / ∑ b [ i ] \sum{a[i]}/\sum{b[i]} ∑a[i]/∑b[i],求最值。
圈地游戏
solution:
首先我们需要将面积映射到边上,然后找到一条回路。
巧妙:我们会发现对于竖直的边或者是水平的边,一定是成对出现,两两相对,不同的边对之间夹着的面积不会重复,所以我们可以人为的修改边权,例如我们将面积映射到竖直方向的边上,对于逆时针旋转的方向,将向下的边权加上前缀面积的负值,向上的边加上前缀面积的正值。
每次我们二分一个 m i d mid mid,去验证 ∑ S − ∑ C ∗ m i d > = 0 \sum S - \sum C*mid >=0 ∑S−∑C∗mid>=0
所以我们去验证当前的图中是否有正权环, s p f a spfa spfa即可
#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define D double
using namespace std;
const int N = 105;
const D eps = 1e-4;
int n,m,id[N][N],cid,tmp;
D sum[N][N],a[N][N],b[N][N],len[N*N],dis[N*N],l,r,ans;
int en[N*N],nex[N*N],lst[N*N],tot;
void add(int x,int y,D z){
en[++tot]=y;nex[tot]=lst[x];lst[x]=tot;len[tot]=z;
}
void init(D mid){
tot=0;memset(lst,0,sizeof lst);
for(int i=0;i<=n;i++)
for(int j=0;j<m;j++){
D x=a[i][j];
add(id[i][j],id[i][j+1],-x*mid);
add(id[i][j+1],id[i][j],-x*mid);
}
for(int i=0;i<n;i++)
for(int j=0;j<=m;j++){
D x=b[i][j];
add(id[i][j],id[i+1][j],-x*mid-sum[i+1][j]);
add(id[i+1][j],id[i][j],-x*mid+sum[i+1][j]);
}
}
queue<int>Q;
int in[N*N],cnt[N*N];
bool spfa(){
for(int i=1;i<=cid;i++)dis[i]=-1e9,cnt[i]=0;
Q.push(1);dis[1]=0;
while(Q.size()){
int u=Q.front();Q.pop();in[u]=0;
for(int i=lst[u];i;i=nex[i]){
int v=en[i];
if(dis[v]<dis[u]+len[i]){
if(++cnt[v]>=tmp)return 1;
dis[v]=dis[u]+len[i];
if(!in[v])Q.push(v),in[v]=1;
}
}
}return 0;
}
signed main()
{
scanf("%d%d",&n,&m);tmp=min(n*m,505);
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
scanf("%lf",&sum[i][j]),sum[i][j]+=sum[i][j-1];
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)id[i][j]=++cid;
for(int i=0;i<=n;i++)
for(int j=0;j<m;j++)scanf("%lf",&a[i][j]);
for(int i=0;i<n;i++)
for(int j=0;j<=m;j++)scanf("%lf",&b[i][j]);
l=0,r=1e6;
while(l+eps<=r){
D mid=(l+r)/2.00;
init(mid);
if(spfa())ans=mid,l=mid;
else r=mid;
}printf("%.3lf\n",ans);
return 0;
}