【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<=100php

Source

Solution

蠻好的一道題,本身想了一段時間仍是能想出來的QwQios

首先黑白染色,而後每一個節點拆出橫豎兩個方向的節點$id_{1}$和$id_{2}$.spa

連邊$S$到黑點,白點連$T$,容量爲$2$,費用爲$0$,限制兩個方向;設計

黑點向黑點的每一個方向連兩條邊,白點的每一個方向向白點連兩條邊,容量都是$1$,費用一條爲$val$,一條爲$0$,表示選擇轉彎或者直道的價值。blog

而後黑點的每一個方向向相鄰白點的每一個方向連邊,容量爲$1$,費用爲$0$。ip

對於一個拐彎格子,會得到$2*val$的價值,對於一個直道格子,會得到$1*val$的價值,因此最後的答案就是$MaxCost-sum$。get

代碼仍是很好寫的,就是一開始細節出了問題WA了兩次QwQstring

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\n",Cost-sum); 
	
	return 0;
}
相關文章
相關標籤/搜索