• BZOJ 4261: 建设游乐场


    4261: 建设游乐场

    Time Limit: 50 Sec  Memory Limit: 256 MB
    Submit: 38  Solved: 16
    [Submit][Status][Discuss]

    Description

    现在有一大块土地,可以看成N*M的方格。在这块土地上,有些格子内是崎岖的山地,无法建造任何东西;其他格子都是平原。现在打算在这块土地上建设一个游乐园。游乐园由若干条闭合的过山车轨道组成,每个平原格子都要铺一截轨道,为下列 6 种类型中的一种:
    (每张图表示一块平原格子,图内网格线为辅助线,无实际意义。)
     
    其中前 2 种为直轨道,后 4 种为弯轨道。显然对游客来说,弯轨道更加刺激。
     
    由于每块格子风景各不相同,经过一番研究,现给了N*M个方格中的每个格子一个评估值,意义为:如果该格子修建弯轨道,会给游客们带来多少的愉悦值。现需要一名设计师,帮他设计一种最优的轨道建设方案,使所有格子给游客们带来的愉悦值之和尽量大。(如果没有合法方案,输出 -1)

    Input

    第一行两个正整数 n, m。
    接下来 n 行,每行 m 个数,描述了整块土地。其中 1 表示山地,0 表示平原。接下来 n 行,每行 m 个非负整数,第 i 行第 j 个为 Vi,j,表示格子 (i,j) 修建弯轨道能给游客们带来的愉悦值。

    Output

    一行一个数,表示最优设计方案中给游客们带来的愉悦值之和。

    Sample Input

    3 3
    1 1 1
    1 0 0
    1 0 0
    48 94 1
    78 78 81
    1 12 60

    Sample Output

    231

    HINT

    N<=150,M<=30,Vi,j<=100

    Source

    分析:

    好题~~~

    考虑构成若干个环的必要条件:每个格子的管道都要链接相邻的两个格子...然后把一个格子拆成两个管子,如果两个管子方向不同,则可以得到价值,如果相同就无法得到...

    既然是网格图,那么我们考虑黑白染色,对于每个必须放的格子,我们把这个格子拆成三个点,一个点叫做控制点,一个代表竖向管道,一个代表横向管道...

    然后对于每个黑点,从$S$向黑点的控制点连边$<S,id[i][j][0],2,0>$,代表连接两个格子,$<id[i][j][0],id[i][j][1],1,val>$,$<id[i][j][0],id[i][j][1],id[i][j][2],1,val>$,$<id[i][j][0],id[i][j][1],1,0>$,$<id[i][j][0],id[i][j][2],1,0>$...

    对于每个白点,从白点的控制点向$T$连边$<id[i][j][0],T,2,0>$,然后$1$、$2$的连边和黑点相反...

    然后对于每个黑点,我们把竖向点向上下的相邻格子个竖点连边$<1,0>$,横向点向左右的横向点连边$<1,0>$...

    这样,如果满流并且黑白点相同的话就一定是合法解,因为代表每个格子都连接了相邻的两个格子...然后如果一个各自选择了相同方向的管道,那么会得到$val$的收益,如果选择不同的,会得到$2*val$的收益,所以求出最大费用之后减去$sum val$就好了...

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    //by NeighThorn
    #define inf 0x3f3f3f3f
    #define min(a,b) a>b?b:a
    using namespace std;
    
    const int N=150+5,maxn=50000+5,maxm=1000000+5;
    
    int n,m,S,T,cnt,tat,tot,sum,mp[N][N],id[N][N][3],val[N][N];
    int w[maxm],fl[maxm],hd[maxn],to[maxm],nxt[maxm],Min[maxn],dis[maxn],vis[maxn],from[maxn];
    int mv[4][2]={1,0,-1,0,0,1,0,-1};
    
    inline int read(void){
    	char ch=getchar();int x=0;
    	while(!(ch>='0'&&ch<='9')) ch=getchar();
    	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    	return x;
    }
    
    inline void add(int x,int y,int l,int s){
    	w[cnt]= s;fl[cnt]=l;to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++;
    	w[cnt]=-s;fl[cnt]=0;to[cnt]=x;nxt[cnt]=hd[y];hd[y]=cnt++;
    }
    
    inline bool spfa(void){
    	memset(Min,inf,sizeof(Min));
    	memset(dis,inf,sizeof(dis));
    	queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
    	while(!q.empty()){
    		int top=q.front();q.pop();vis[top]=0;
    		for(int i=hd[top];i!=-1;i=nxt[i])
    			if(dis[to[i]]>dis[top]+w[i]&&fl[i]){
    				from[to[i]]=i;
    				dis[to[i]]=dis[top]+w[i];
    				Min[to[i]]=min(Min[top],fl[i]);
    				if(!vis[to[i]])
    					vis[to[i]]=1,q.push(to[i]);
    			}
    	}
    	return dis[T]!=inf;
    }
    
    inline int find(void){
    	for(int i=T;i!=S;i=to[from[i]^1])
    		fl[from[i]]-=Min[T],fl[from[i]^1]+=Min[T];
    	return Min[T]*dis[T];
    }
    
    inline int mcmf(void){
    	int res=0,flow=0;
    	while(spfa())
    		res+=find(),flow+=Min[T];
    	if(flow!=tot) return -1;
    	return -res-sum;
    }
    
    signed main(void){
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			mp[i][j]=read();
    	for(int i=0;i<=n+1;i++) mp[i][0]=mp[i][m+1]=1;
    	for(int i=0;i<=m+1;i++) mp[0][i]=mp[n+1][i]=1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			val[i][j]=read();
    			if(!mp[i][j])
    				sum+=val[i][j];
    		}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(!mp[i][j])
    				for(int k=0;k<3;k++)
    					id[i][j][k]=++tot;
    	S=0,T=++tot;tot=0;
    	memset(hd,-1,sizeof(hd));
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(!mp[i][j]){
    				if((i+j)&1)
    					tat+=2, 
    					add(S,id[i][j][0],2,0),
    					add(id[i][j][0],id[i][j][1],1,0),
    					add(id[i][j][0],id[i][j][2],1,0),
    					add(id[i][j][0],id[i][j][1],1,-val[i][j]),
    					add(id[i][j][0],id[i][j][2],1,-val[i][j]);
    				else
    					tot+=2,
    					add(id[i][j][0],T,2,0),
    					add(id[i][j][1],id[i][j][0],1,0),
    					add(id[i][j][2],id[i][j][0],1,0),
    					add(id[i][j][1],id[i][j][0],1,-val[i][j]),
    					add(id[i][j][2],id[i][j][0],1,-val[i][j]);
    			}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(!mp[i][j]&&((i+j)&1)){
    				for(int k=0,x,y;k<4;k++){
    					x=i+mv[k][0],y=j+mv[k][1];
    					if(!mp[x][y]){
    						if(k<2)
    							add(id[i][j][1],id[x][y][1],1,0);
    						else
    							add(id[i][j][2],id[x][y][2],1,0);
    					}
    				}
    			}
    	if(tat!=tot) return puts("-1"),0;
    	printf("%d
    ",mcmf());
    	return 0;
    }
    

      


    By NeighThorn

  • 相关阅读:
    P1522 牛的旅行
    P1908 逆序对
    P1107 雷涛的小猫
    欧拉函数
    P2679 子串
    P1063 能量项链
    P1052 过河
    P1020 导弹拦截
    P1330 阳光封锁大学
    P1198 最大数
  • 原文地址:https://www.cnblogs.com/neighthorn/p/6603322.html
Copyright © 2020-2023  润新知