【BZOJ3052】【WC2013】糖果公园
【BZOJ2631】tree

【BZOJ3232】圈地游戏

Zarxdy34 posted @ 2016年3月09日 19:33 in BZOJ with tags 网络流 , 479 阅读

  网络流,最小割模型。

  从S向每个点连一条容量为该点价值的边,边界上的点向T连一条容量为该点在边界上的边权和的边,相邻两点连一条容量为两点之间的边的边权的边(好绕口...)。

  就是把被圈住的格点在集合S里,其他的在集合T里。(好像说反了?)

  跑最大流就好了。

  我比较懒几条可以合并的边没有合并。

  大常数代码需要19秒,原因不明,待会儿再改。

 

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=55,maxv=maxn*maxn;
const double inf=1e12,eps=1e-8;
 
int head[maxv],next[maxv<<5],E[maxv<<5],Ecnt,PEcnt;
double Flow[maxv<<5];
int V[maxn][maxn],ROW[maxn][maxn],COL[maxn][maxn];
int Vcnt,S,T;
int n,m,Vsum;
 
int cur[maxv];
int h[maxv];
int Q[maxv],vis[maxv],top,tail;
 
bool BFS()
{
    memset(h,0,sizeof(h));
    memset(vis,0,sizeof(vis));
    for (int i=1;i<=T;i++) cur[i]=head[i];
    Q[top=tail=1]=S;vis[S]=1;
    while (top<=tail)
    {
        int x=Q[top++];
        for (int i=head[x];i;i=next[i]) if (!vis[E[i]] && Flow[i^1]>eps)
            Q[++tail]=E[i],vis[E[i]]=1,h[E[i]]=h[x]+1;
    }
    return h[T];
}
 
double DFS(int x,double flow)
{
    if (x==T) return flow;
    double rest=flow,nowflow=0;
    for (int &i=cur[x];i && rest>eps;i=next[i]) if (h[E[i]]==h[x]+1 && Flow[i^1]>eps)
    {
        double d=DFS(E[i],min(rest,Flow[i^1]));
        Flow[i]+=d;Flow[i^1]-=d;
        rest-=d;
        nowflow+=d;
    }
    return nowflow;
}
 
bool Check()
{
    double res=0;
    while (BFS()) res+=DFS(S,inf);
    return Vsum-res>eps;
}
 
void Add_Edge(int x,int y,int c) {next[++Ecnt]=head[x];head[x]=Ecnt;E[Ecnt]=y;Flow[Ecnt]=0;next[++Ecnt]=head[y];head[y]=Ecnt;E[Ecnt]=x;Flow[Ecnt]=c;}
int p(int x,int y) {if (x==0 || x==n+1 || y==0 || y==m+1) return T;return (x-1)*m+y;}
 
int main()
{
    scanf("%d%d",&n,&m);
    S=n*m+1;T=S+1;Ecnt=1;
    for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&V[i][j]),Add_Edge(S,p(i,j),V[i][j]),Vsum+=V[i][j];
    PEcnt=Ecnt+1;
    for (int i=1;i<=n+1;i++) for (int j=1;j<=m;j++) scanf("%d",&ROW[i][j]);
    for (int i=1;i<=n;i++) for (int j=1;j<=m+1;j++) scanf("%d",&COL[i][j]);
    for (int i=1;i<=n;i++) for (int j=1;j<=m;j++)
    {
        Add_Edge(p(i,j),p(i-1,j),ROW[i][j]);
        Add_Edge(p(i,j),p(i+1,j),ROW[i+1][j]);
        Add_Edge(p(i,j),p(i,j-1),COL[i][j]);
        Add_Edge(p(i,j),p(i,j+1),COL[i][j+1]);
    }
    double l=0,r=250000;
    while (r-l>1e-5)
    {
        double mid=(l+r)/2.0;
        for (int i=PEcnt;i<=Ecnt;i+=2) Flow[i^1]*=mid;
        if (Check()) l=mid;else r=mid;
        for (int i=PEcnt;i<=Ecnt;i+=2) (Flow[i^1]+=Flow[i])/=mid,Flow[i]=0;
        for (int i=2;i<PEcnt;i+=2) Flow[i^1]+=Flow[i],Flow[i]=0;
    }
    printf("%.3lf",l);
    return 0;
}

 

  下面这份代码跑得很快但是不知道为什么WA了。本地上能过,求指正。

  发现错误了,是精度不够...原来是r-l>1e-5,改成r-l>1e-6就好了...日死BZOJ...

 

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=55,maxv=maxn*maxn;
const double inf=1e12,eps=1e-8;

void read(int &x) {char ch;while ((ch=getchar())<'0' || ch>'9');x=ch-'0';while ((ch=getchar())<='9' && ch>='0') x=x*10+ch-'0';}

int head[maxv],next[maxv<<5],E[maxv<<5],Ecnt,PEcnt;
double Flow[maxv<<5],PreF[maxv<<5];
int V[maxn][maxn],ROW[maxn][maxn],COL[maxn][maxn];
int Vcnt,S,T;
int n,m,Vsum;

int cur[maxv];
int h[maxv];
int Q[maxv],vis[maxv],top,tail;

bool BFS()
{
    memset(h,0,sizeof(h));
    memset(vis,0,sizeof(vis));
    memcpy(cur,head,sizeof(cur));
    Q[top=tail=1]=S;vis[S]=1;
    while (top<=tail)
    {
        int x=Q[top++];
        for (int i=head[x];i;i=next[i]) if (!vis[E[i]] && Flow[i^1]>eps)
        {
        	Q[++tail]=E[i];
			vis[E[i]]=1;
			h[E[i]]=h[x]+1;
			if (E[i]==T) return true;
		}
    }
    return false;
}

double argu;

bool DFS(int x,double flow)
{
    if (x==T) {argu=flow;return true;}
    for (int &i=cur[x];i;i=next[i]) if (h[E[i]]==h[x]+1 && Flow[i^1]>eps)
    	if (DFS(E[i],min(flow,Flow[i^1])))
    	{
		    Flow[i]+=argu,Flow[i^1]-=argu;
		    return true;
    	}
    return false;
}

bool Check()
{
    double res=0;
    while (BFS()) while (DFS(S,inf)) res+=argu;
    return Vsum-res>eps;
}

void Add_Edge(int x,int y,int c1,int c2) {next[++Ecnt]=head[x];head[x]=Ecnt;E[Ecnt]=y;Flow[Ecnt]=c1;next[++Ecnt]=head[y];head[y]=Ecnt;E[Ecnt]=x;Flow[Ecnt]=c2;}
int p(int x,int y) {if (x==0 || x==n+1 || y==0 || y==m+1) return S;return (x-1)*m+y;}

int main()
{
    scanf("%d%d",&n,&m);
    S=n*m+1;T=S+1;Ecnt=1;
    for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) read(V[i][j]),Add_Edge(p(i,j),T,0,V[i][j]),Vsum+=V[i][j];
    PEcnt=Ecnt+1;
    for (int i=1;i<=n+1;i++) for (int j=1;j<=m;j++) read(ROW[i][j]);
    for (int i=1;i<=n;i++) for (int j=1;j<=m+1;j++) read(COL[i][j]);
    Add_Edge(S,p(1,1),0,ROW[1][1]+COL[1][1]);
    Add_Edge(S,p(1,m),0,ROW[1][m]+COL[1][m+1]);
    Add_Edge(S,p(n,1),0,ROW[n+1][1]+COL[n][1]);
    Add_Edge(S,p(n,m),0,ROW[n+1][m]+COL[n][m+1]);
    for (int i=2;i<m;i++) Add_Edge(S,p(1,i),0,ROW[1][i]),Add_Edge(S,p(n,i),0,ROW[n+1][i]);
    for (int i=2;i<n;i++) Add_Edge(S,p(i,1),0,COL[i][1]),Add_Edge(S,p(i,m),0,COL[i][m+1]);
    for (int i=1;i<=n;i++) for (int j=1;j<=m;j++)
    {
    	if (i<n) Add_Edge(p(i,j),p(i+1,j),ROW[i+1][j],ROW[i+1][j]);
    	if (j<m) Add_Edge(p(i,j),p(i,j+1),COL[i][j+1],COL[i][j+1]);
    }
    double l=0,r=Vsum;
    memcpy(PreF,Flow,sizeof(Flow));
    while (r-l>1e-6)
    {
        double mid=(l+r)/2.0;
        for (int i=2;i<PEcnt;i++) Flow[i]=PreF[i];
        for (int i=PEcnt;i<=Ecnt;i++) Flow[i]=PreF[i]*mid;
        if (Check()) l=mid;else r=mid;
    }
    printf("%.3f",l);
    return 0;
}

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter