bzoj-3232 圈地游戏

来源:互联网 发布:惠喵 知乎百度知道 编辑:程序博客网 时间:2024/04/28 08:33

题意:

在一个n*m的网格里,边上有花费,格里有权值;
从任意一个点开始绕一圈,绕一个简单环出来,里面的所有格子就是收益;
求最大的收益/花费;

所有数<=100;


题解:

考虑01分数规划的方式,但是花费和权值不在一起;

那么考虑将格内的权值转化到边上;

实际上将边有向化,按边方向左面一行的权值为正,右面为负,加起来作为选了这条边的权值;

这样在围成一个环的过程中会将外面的点消掉,而里面的点是四倍大小;

问题转化成二分答案,对一个图进行check,考虑其中有没有正权的环;

上一个BF判断就好了;

时间复杂度O(n^2*m^2*log400);

二分上界定为400就可以了,别忘了答案要除4;


代码:


#include<stdio.h>#include<string.h>#include<algorithm>#define N 110using 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;elser=mid;}printf("%.3lf",l/4);return 0;}



0 0
原创粉丝点击