1. 程式人生 > >【BZOJ 3232】圈地遊戲 二分+SPFA判環/最小割經典模型

【BZOJ 3232】圈地遊戲 二分+SPFA判環/最小割經典模型

detail blank 簡單 tails double 就是 inline ref 如果

最小割經典模型指的是“一堆元素進行選取,對於某個元素的取舍有代價或價值,對於某些對元素,選取後會有額外代價或價值”的經典最小割模型,建立倒三角進行最小割。
這個二分是顯然的,一開始我也是想到了最小割的那個模型的但是我覺得他會不是一個圈我就否掉了,但是仔細想想的話會發現,如果是這樣的話所得到的答案一定小於等於一個圈的答案(濃度),所以我們可定會得到最終答案,所以這樣做是可以的,所以說要有寬松得正解的意識(泥沙俱下但沙子不影響我泥)。當時我否掉最小割以後就立馬去想費用流了,然後想到建圖後發現那樣建圖雖然不好跑費用流,但是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; }

【BZOJ 3232】圈地遊戲 二分+SPFA判環/最小割經典模型