zoukankan      html  css  js  c++  java
  • 【BZOJ-4261】建设游乐场 最大费用最大流

    4261: 建设游乐场

    Time Limit: 50 Sec  Memory Limit: 256 MB
    Submit: 21  Solved: 8
    [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

    Solution

    蛮好的一道题,自己想了一段时间还是能想出来的QwQ

    首先黑白染色,然后每个节点拆出横竖两个方向的节点$id_{1}$和$id_{2}$.

    连边$S$到黑点,白点连$T$,容量为$2$,费用为$0$,限制两个方向;

    黑点向黑点的每个方向连两条边,白点的每个方向向白点连两条边,容量都是$1$,费用一条为$val$,一条为$0$,表示选择转弯或者直道的价值。

    然后黑点的每个方向向相邻白点的每个方向连边,容量为$1$,费用为$0$。

    对于一个拐弯格子,会获得$2*val$的价值,对于一个直道格子,会获得$1*val$的价值,所以最后的答案就是$MaxCost-sum$。

    代码还是很好写的,就是一开始细节出了问题WA了两次QwQ

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    using namespace std;
    inline int read()
    {
    	int x=0,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();}
    	return x*f;
    }
    
    #define MAXN 30010
    #define INF 0x7fffffff
    
    int N,M,mp[200][50],val[200][50],sum,tot;
    
    struct EdgeNode{
    	int next,to,cap,cost;
    }edge[500010];
    int head[MAXN],cnt=1;
    inline void AddEdge(int u,int v,int w,int c) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].cap=w; edge[cnt].cost=c;}
    inline void InsertEdge(int u,int v,int w,int c) {AddEdge(u,v,w,c); AddEdge(v,u,0,-c);}
    
    int Cost,S,T,dis[MAXN],mark[MAXN];
    queue<int>q;
    inline bool spfa()
    {
        for (int i=S; i<=T; i++) dis[i]=-INF,mark[i]=0;
        queue<int>q;
        q.push(S); dis[S]=0; mark[S]=1;
        while (!q.empty()) {
            int now=q.front(); q.pop(); mark[now]=0;
            for (int i=head[now]; i; i=edge[i].next)
                if (edge[i].cap && dis[edge[i].to]<dis[now]+edge[i].cost) {
                        dis[edge[i].to]=dis[now]+edge[i].cost;
                        if (!mark[edge[i].to]) q.push(edge[i].to),mark[edge[i].to]=1;
                }
        }
        return dis[T]!=-INF;
    }
    
    inline int dfs(int loc,int low)
    {
        mark[loc]=1;
        if (loc==T) return low;
        int used=0,w;
        for (int i=head[loc]; i; i=edge[i].next)
            if (edge[i].cap && !mark[edge[i].to] && dis[edge[i].to]==dis[loc]+edge[i].cost) {
                w=dfs(edge[i].to,min(edge[i].cap,low-used));
                edge[i].cap-=w; edge[i^1].cap+=w; used+=w; Cost+=w*edge[i].cost;
                if (low==used) return used;
            }
        return used;
    }
    
    inline int zkw()
    {
        int re=0;
        while (spfa()) {
            mark[T]=1;
            while (mark[T]) {
                for (int i=S; i<=T; i++) mark[i]=0;
                re+=dfs(S,INF);
            }
        }
        return re;
    }
    
    int col[200][50],id[200][50][3],ID,dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
    //id 0 主点 id 1 竖方向 id 3 横方向 
    inline bool Check(int x,int y) {return x>=1&&x<=N&&y>=1&&y<=M;}
    int main()
    {
    	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=1; i<=N; i++)
    		for (int j=1; j<=M; j++)
    			val[i][j]=read(),sum+=mp[i][j]? 0:val[i][j],col[i][j]=(i+j)&1;
    	
    	for (int i=1; i<=N; i++)
    		for (int j=1; j<=M; j++)
    			for (int k=0; k<=2; k++) id[i][j][k]=++ID;
    			
    	S=0; T=++ID;
    	
    	for (int i=1; i<=N; i++)
    		for (int j=1; j<=M; j++)
    			if (!mp[i][j]) {
    				if (col[i][j]) {
    					InsertEdge(S,id[i][j][0],2,0);
    					InsertEdge(id[i][j][0],id[i][j][1],1,val[i][j]);
    					InsertEdge(id[i][j][0],id[i][j][1],1,0);
    					InsertEdge(id[i][j][0],id[i][j][2],1,val[i][j]);
    					InsertEdge(id[i][j][0],id[i][j][2],1,0);
    				} else {
    					tot+=2;
    					InsertEdge(id[i][j][0],T,2,0); 
    					InsertEdge(id[i][j][1],id[i][j][0],1,val[i][j]);
    					InsertEdge(id[i][j][1],id[i][j][0],1,0);
    					InsertEdge(id[i][j][2],id[i][j][0],1,val[i][j]);
    					InsertEdge(id[i][j][2],id[i][j][0],1,0);
    				}
    			}
    	
    	for (int i=1; i<=N; i++)
    		for (int j=1; j<=M; j++)
    			if (!mp[i][j] && col[i][j])
    				for (int k=0; k<=3; k++) {
    					int tx=i+dx[k],ty=j+dy[k];
    					if (Check(tx,ty) && !mp[tx][ty])
    						if (k==0 || k==2)
    							InsertEdge(id[i][j][1],id[tx][ty][1],1,0);
    						else 
    							InsertEdge(id[i][j][2],id[tx][ty][2],1,0);
    				} 
    	
    	int flow=zkw();
    	
    	if (flow<tot) return puts("-1"),0;
    	
    	printf("%d
    ",Cost-sum); 
    	
    	return 0;
    }
    

      

  • 相关阅读:
    linux 权限管理命令
    大三上学期总结
    C# 读写Excel的一些方法,Aspose.Cells.dll
    Topshelf 创建.net服务整理和安装步骤(转)
    你必须知道的.NET之特性和属性(转)
    用SQL语句删除一个数据库的所有表和所有存储过程
    System.DllNotFoundException: 无法加载 DLL“FileTracker.dll”: 动态链接库(DLL)初始化例
    关于消息队列的使用[转]
    PhpStorm使用技巧小结
    转载]C#实现获取浏览器信息
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/6434649.html
Copyright © 2011-2022 走看看