[Bzoj3232]圈地遊戲(分數規劃+最小割/spfa判負環)
阿新 • • 發佈:2018-11-08
Description
DZY家的後院有一塊地,由N行M列的方格組成,格子內種的菜有一定的價值,並且每一條單位長度的格線有一定的費用。 DZY喜歡在地裡散步。他總是從任意一個格點出發,沿著格線行走直到回到出發點,且在行走途中不允許與已走過的路線有任何相交或觸碰(出發點除外)。記這條封閉路線內部的格子總價值為V,路線上的費用總和為C,DZY想知道V/C的最大值是多少。Input
第一行為兩個正整數n,m。 接下來n行,每行m個非負整數,表示對應格子的價值。 接下來n+1行,每行m個正整數,表示所有橫向的格線上的費用。 接下來n行,每行m+1個正整數,表示所有縱向的格線上的費用。 (所有資料均按從左到右,從上到下的順序輸入,參見樣例和配圖)Output
Sample Input
3 41 3 3 3
1 3 1 1
3 3 1 0
100 1 1 1
97 96 1 1
1 93 92 92
1 1 90 90
98 1 99 99 1
95 1 1 1 94
1 91 1 1 89
Sample Output
1.286 這道題調了很久,為了複習dinic特意在寫這題前先去寫一遍格子取數(然後調了一晚上)。 不難發現這道題具有分數規劃的性質。我們要求格子的和處以邊的和。具體的分數規劃可以參考我的另一篇部落格#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define maxn 55 #define INF 99999999.999999 #define eps 1e-6 using namespace std; inline void read(int &x){ x=0;int f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=f; } int N,M; double ans,sum; int p(int a,int b){ return (a-1)*M+b; } int map[maxn][maxn]; int up[maxn][maxn],ls[maxn][maxn]; struct node{ int nex,to; double w; }edge[500000]; int head[3000],tot; int S,T; inline void insert(int from,int to,double w){ edge[tot].nex=head[from]; head[from]=tot; edge[tot].to=to; edge[tot].w=w; tot++; edge[tot].nex=head[to]; head[to]=tot; edge[tot].to=from; edge[tot].w=0; tot++; } int dept[3000]; bool bfs(){ memset(dept,0,sizeof(dept)); queue<int>que; que.push(S); dept[S]=1; int x; while(!que.empty()){ x=que.front(); que.pop(); for(int i=head[x];i!=-1;i=edge[i].nex) if(edge[i].w>eps&&!dept[edge[i].to]){ dept[edge[i].to]=dept[x]+1; que.push(edge[i].to); if(edge[i].to==T) return 1; } } return 0; } double dfs(int x,double f){ if(x==T) return f; double tmp=f; for(int i=head[x];i!=-1;i=edge[i].nex) if(edge[i].w>eps&&dept[edge[i].to]==dept[x]+1){ double cal=dfs(edge[i].to,min(tmp,edge[i].w)); if(cal<eps) dept[edge[i].to]=0; edge[i].w-=cal; edge[i^1].w+=cal; tmp-=cal; if(tmp<eps) break ; } return f-tmp; } void dinic(){ while(bfs()){ //for(int i=1;i<=N*M;i++) // cout<<dept[i]<<" "; //cout<<endl; //printf("%.3f\n",ans); //system("pause"); ans+=dfs(S,INF); } } bool check(double x){ memset(head,-1,sizeof(head)); tot=0;ans=0.0; for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) insert(S,p(i,j),1.0*map[i][j]); for(int i=1;i<N;i++) for(int j=1;j<=M;j++){ insert(p(i,j),p(i+1,j),x*up[i][j]); insert(p(i+1,j),p(i,j),x*up[i][j]); } for(int i=1;i<=N;i++) for(int j=1;j<M;j++){ insert(p(i,j),p(i,j+1),x*ls[i][j]); insert(p(i,j+1),p(i,j),x*ls[i][j]); } for(int i=1;i<=M;i++){ insert(p(1,i),T,x*up[0][i]); insert(p(N,i),T,x*up[N][i]); } for(int i=1;i<=N;i++){ insert(p(i,1),T,x*ls[i][0]); insert(p(i,M),T,x*ls[i][M]); } dinic(); //cout<<sum<<" "; //printf("%.3f\n",ans); return sum-ans>eps; } int main(){ //freopen("10.in","r",stdin); read(N);read(M); S=0;T=N*M+1; for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) read(map[i][j]),sum+=1.0*map[i][j]; for(int i=0;i<=N;i++) for(int j=1;j<=M;j++) read(up[i][j]); for(int i=1;i<=N;i++) for(int j=0;j<=M;j++) read(ls[i][j]); double l=0.0,r=N*M*100.0,mid; while(r-l>eps) { mid=(l+r)*0.5; //cout<<l<<" "<<r<<" "<<mid<<endl; if(check(mid)) l=mid; else r=mid; } printf("%.3lf",l); return 0; } /* 3 4 1 3 3 3 1 3 1 1 3 3 1 0 100 1 1 1 97 96 1 1 1 93 92 92 1 1 90 90 98 1 99 99 1 95 1 1 1 94 1 91 1 1 89 */View Code