【BZOJ 3232】圈地游戏 二分+SPFA判环/最小割经典模型
最小割经典模型指的是“一堆元素进行选取,对于某个元素的取舍有代价或价值,对于某些对元素,选取后会有额外代价或价值”的经典最小割模型,建立倒三角进行最小割。
这个二分是显然的,一开始我也是想到了最小割的那个模型的但是我觉得他会不是一个圈我就否掉了,但是仔细想想的话会发现,如果是这样的话所得到的答案一定小于等于一个圈的答案(浓度),所以我们可定会得到最终答案,所以这样做是可以的,所以说要有宽松得正解的意识(泥沙俱下但沙子不影响我泥)。当时我否掉最小割以后就立马去想费用流了,然后想到建图后发现那样建图虽然不好跑费用流,但是SPFA判环还是很劲的,所以我就判了一发环。
在这里就顺便说一下SPFA判负(正)环吧。DFS的话就是判断一个点是否重复出现在DFS路径中,他有一个优化(没看呢),就叫他DFS+吧。然后他还有BFS版的,就是判断一个点是否重复入队n次(点数),但是不能判断是否被更新n次,这样有可能会出错(不用重边就可以做到)(也许可以分析是否可行但是不会很简单而且很难考虑周全),并且这两种方法的时间复杂度有些时候差距并不大只不过是一个常数。网上还有人说是进队次数大于入度,这个经试验证明是扯淡。还有另一种做法是判断到达次点的最短路径的边数等于n这个不仅很对还很快,就叫他BFS+吧。
对于这道题DFS会T,然而DFS+,BFS,以及BFS+均可过,而BFS+表现最优。这说明虽然找最短路方面BFS_SPFA找最短路比DFS_SPFA要好,但是在判环方面并不是DFS一定优于BFS,比如这道题,所以说BFS大法吼。
#include <cstdio> #include <cstring> #include <algorithm> #define pos(a,b) (((a)-1)*(m+1)+(b)) typedef long double db; const int N=55; const int P=N*N; const int E=P*10; const db oo=-1e18; const db eps=1e-9; const db ans_eps=1e-6; struct V{ int to,next; db w; }c[E]; int head[P],t; inline void add(int x,int y,db z){ c[++t].to=y,c[t].next=head[x],head[x]=t,c[t].w=z; } db dis[P]; bool in[P]; int n,m,sum; int val[N][N],cost1[N][N],cost2[N][N]; int q[P],front,back; int cnt[P]; inline bool spfa(int s){ q[back++]=s,in[s]=true; if(back==P)back=0; while(front!=back){ int x=q[front++]; in[x]=false; if(front==P)front=0; for(int i=head[x];i;i=c[i].next) if(dis[x]+c[i].w-dis[c[i].to]>eps){ dis[c[i].to]=dis[x]+c[i].w; cnt[c[i].to]=cnt[x]+1; if(cnt[c[i].to]==sum)return true; if(in[c[i].to]==false){ q[back++]=c[i].to; in[c[i].to]=true; if(back==P)back=0; } } } return false; } inline bool check(db mid){ memset(head,0,sizeof(head)),t=0; memset(in,0,sizeof(in)); memset(cnt,0,sizeof(cnt)); for(int i=1;i<=sum;++i)dis[i]=oo; for(int i=1;i<=n+1;++i) for(int j=1;j<=m+1;++j){ if(j!=m+1) add(pos(i,j),pos(i,j+1),-cost1[i][j]*mid+val[i][j]); if(j!=1) add(pos(i,j),pos(i,j-1),-cost1[i][j-1]*mid-val[i][j-1]); if(i!=n+1) add(pos(i,j),pos(i+1,j),-cost2[i][j]*mid); if(i!=1) add(pos(i,j),pos(i-1,j),-cost2[i-1][j]*mid); } dis[sum/2]=0.; return spfa(sum/2); } int main(){ scanf("%d%d",&n,&m); sum=(n+1)*(m+1); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",&val[i][j]); for(int i=1;i<=m;++i) for(int j=n;j>0;--j) val[j][i]+=val[j+1][i]; for(int i=1;i<=n+1;++i) for(int j=1;j<=m;++j) scanf("%d",&cost1[i][j]); for(int i=1;i<=n;++i) for(int j=1;j<=m+1;++j) scanf("%d",&cost2[i][j]); db l=0.,r=200.,mid,ans=0.; while(l+ans_eps<r){ mid=(l+r)*0.5; if(check(mid)) ans=mid,l=mid; else r=mid; } printf("%.3f",(double)ans); return 0; }
苟利国家生死以, 岂因祸福避趋之。