bzoj 3232 圈地游戏——0/1分数规划(或网络流)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3232
当然是0/1分数规划。但加的东西和减的东西不在一起,怎么办?
考虑把它们合在一起。因为边围成的形状像一个环,所以把格子的贡献也放到边上,然后正常判环。
放到边上的方法就是:比如竖着的边,可以在每一行上维护该行格子值前缀和,然后指定那个围成的形状是,比如,逆时针的,那么向上的边就加上到它为止的前缀值,向下的边就减去到它为止的前缀值,然后就能判环了!
这样一定只有一个环。但多个环答案不会更优。
还可以用网络流。与 s 相连表示选、与 t 相连表示不选的话,每个点到 s 连该点权值的边,到 t 连边权为0的边,相邻点之间连它们夹着的边权值的边,这样如果相邻的点一个选了一个没选,就得割它们之间的那条边,就能表示了。
自己写了判环的那个。
注意如果以竖着的边算了围住的部分,就不要再用横着的边同时算了!!
请把 eps 设成 1e-7 而不是 1e-5 。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define db double using namespace std; const int N=55,M=N*N; const db eps=1e-7; int n,m,fl[N][N]/*,fu[N][N]*/,eh[N][N],el[N][N],cnt[N][N],tot; db l,r,mid,ans,dis[N][N],w[N][N][5]; bool vis[N][N]; queue<pair<int,int> > q; bool spfa() { // printf("mid=%.3lf\n",mid); while(q.size())q.pop(); for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) { q.push(make_pair(i,j)); vis[i][j]=1; dis[i][j]=0; cnt[i][j]=0; if(i) { w[i][j][0]=fl[i][j]-mid*el[i][j]; // if(mid<3&&mid>2&&el[i][j]==1) // printf("w[%d][%d][0]=%.3lf\n",i,j,w[i][j][0]); } if(j) { w[i][j][1]=/*-fu[i][j]*/-mid*eh[i][j]; // if(mid<3&&mid>2&&eh[i][j]==1) // printf("w[%d][%d][1]=%.3lf\n",i,j,w[i][j][1]); } if(i<n) { w[i][j][3]=-fl[i+1][j]-mid*el[i+1][j]; // if(mid<3&&mid>2&&el[i+1][j]==1) // printf("w[%d][%d][3]=%.3lf\n",i,j,w[i][j][3]); } if(j<m) { w[i][j][2]=/*fu[i][j+1]*/-mid*eh[i][j+1]; // if(mid<3&&mid>2&&eh[i][j+1]==1) // printf("w[%d][%d][2]=%.3lf\n",i,j,w[i][j][2]); } } while(q.size()) { int x=q.front().first,y=q.front().second; q.pop(); vis[x][y]=0; // if(mid>2&&mid<3)printf("x=%d y=%d cnt=%d dis=%.3lf\n",x,y,cnt[x][y],dis[x][y]); // if(mid>2&&mid<3)printf("fa[%d][%d]=(%d,%d)\n",x,y,fa[x][y][0],fa[x][y][1]); if(x&&dis[x-1][y]<dis[x][y]+w[x][y][0]) { dis[x-1][y]=dis[x][y]+w[x][y][0]; // printf(" w[%d][%d][0]=%.3lf\n",x,y,w[x][y][0]); // fa[x-1][y][0]=x; fa[x-1][y][1]=y; cnt[x-1][y]=cnt[x][y]+1; if(cnt[x-1][y]==tot) { // if(mid>2&&mid<3) // printf("x-1=%d y=%d dis=%.3lf\n",x-1,y,dis[x-1][y]); return 1; } if(!vis[x-1][y]) vis[x-1][y]=1,q.push(make_pair(x-1,y)); } if(y&&dis[x][y-1]<dis[x][y]+w[x][y][1]) { dis[x][y-1]=dis[x][y]+w[x][y][1]; // printf(" w[%d][%d][1]=%.3lf\n",x,y,w[x][y][1]); // fa[x][y-1][0]=x; fa[x][y-1][1]=y; cnt[x][y-1]=cnt[x][y]+1; if(cnt[x][y-1]==tot) { // if(mid>2&&mid<3) // printf("x=%d y-1=%d dis=%.3lf\n",x,y-1,dis[x][y-1]); return 1; } if(!vis[x][y-1]) vis[x][y-1]=1,q.push(make_pair(x,y-1)); } if(x<n&&dis[x+1][y]<dis[x][y]+w[x][y][3]) { dis[x+1][y]=dis[x][y]+w[x][y][3]; // printf(" w[%d][%d][3]=%.3lf\n",x,y,w[x][y][3]); // fa[x+1][y][0]=x; fa[x+1][y][1]=y; cnt[x+1][y]=cnt[x][y]+1; if(cnt[x+1][y]==tot) { // if(mid>2&&mid<3) // printf("x+1=%d y=%d dis=%.3lf\n",x+1,y,dis[x+1][y]); return 1; } if(!vis[x+1][y]) vis[x+1][y]=1,q.push(make_pair(x+1,y)); } if(y<m&&dis[x][y+1]<dis[x][y]+w[x][y][2]) { dis[x][y+1]=dis[x][y]+w[x][y][2]; // printf(" w[%d][%d][2]=%.3lf\n",x,y,w[x][y][4]); // fa[x][y+1][0]=x; fa[x][y+1][1]=y; cnt[x][y+1]=cnt[x][y]+1; if(cnt[x][y+1]==tot) { // if(mid>2&&mid<3) // printf("x=%d y+1=%d dis=%.3lf\n",x,y+1,dis[x][y+1]); return 1; } if(!vis[x][y+1]) vis[x][y+1]=1,q.push(make_pair(x,y+1)); } } return 0; } int main() { scanf("%d%d",&n,&m); tot=(n+1)*(m+1);//+1!!! for(int i=1;i<=n;i++) for(int j=1,d;j<=m;j++) { scanf("%d",&d); r+=d; fl[i][j]=fl[i][j-1]+d; // fu[i][j]=fu[i-1][j]+d; } for(int i=0;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&eh[i][j]); for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) scanf("%d",&el[i][j]); while(r-l>eps) { mid=(l+r)/2; if(spfa()) ans=mid,l=mid+eps; else r=mid-eps; } printf("%.3lf\n",ans); return 0; }