1. 程式人生 > >bzoj 3232 圈地遊戲——0/1分數規劃(或網絡流)

bzoj 3232 圈地遊戲——0/1分數規劃(或網絡流)

pop fine printf 註意 維護 %d 貢獻 spa 答案

題目: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; }

bzoj 3232 圈地遊戲——0/1分數規劃(或網絡流)