BZOJ2132 圈地计划 【最小割】

题目

最近房地产商GDOI(Group of Dumbbells Or Idiots)从NOI(Nuts Old Idiots)手中得到了一块开发土地。据了解,

这块土地是一块矩形的区域,可以纵横划分为N×M块小区域。GDOI要求将这些区域分为商业区和工业区来开发。根

据不同的地形环境,每块小区域建造商业区和工业区能取得不同的经济价值。更具体点,对于第i行第j列的区域,

建造商业区将得到Aij收益,建造工业区将得到Bij收益。另外不同的区域连在一起可以得到额外的收益,即如果区

域(I,j)相邻(相邻是指两个格子有公共边)有K块(显然K不超过4)类型不同于(I,j)的区域,则这块区域能增加k

×Cij收益。经过Tiger.S教授的勘察,收益矩阵A,B,C都已经知道了。你能帮GDOI求出一个收益最大的方案么?

输入格式

输入第一行为两个整数,分别为正整数N和M,分别表示区域的行数和列数;

第2到N+1列,每行M个整数,表示商业区收益矩阵A;

第N+2到2N+1列,每行M个整数,表示工业区收益矩阵B;

第2N+2到3N+1行,每行M个整数,表示相邻额外收益矩阵C。

任何数字不超过1000”的限制

输出格式

输出只有一行,包含一个整数,为最大收益值。

输入样例

3 3

1 2 3

4 5 6

7 8 9

9 8 7

6 5 4

3 2 1

1 1 1

1 3 1

1 1 1

输出样例

81

提示

【数据规模】

对于100%的数据有N,M≤100

题解

类似BZOJ2127happiness

上一次似乎没有写博,,

一个经典的最小割模型,每个人有两个选择,每个选择有不同收益,当一些人选择相同时会有额外的收益,求最大收益

用一个这样的图:

这个图有两种割法

设二人同选\(A\)的额外收益为\(w_a\),同选\(B\)为\(w_b\)

①当二者选择不同时,除了没选的收益外,会付出额外代价\(w_a + w_b\)

对应的图中代价,要割掉不同侧的边,以及中间的一条边

\[x_1 + x_3 + x_6\]

\[x_2 + x_4 + x_5\]

②选择相同时,除了

对应图中,割掉一侧的边即可

\[x_1 + x_5\]

\[x_2 + x_6\]

那么有:

\[x_1 + x_3 + x_6 = w_a + w_b\]

\[x_2 + x_4 + x_5 = w_a + w_b\]

\[x_1 + x_5 = w_b\]

\[x_2 + x_6 = w_a\]

似乎有多解,我们不妨设:

\[x_1 = x_5\]

\[x_2 = x_6\]

即可得到一组比较特殊的解:

\[x_1 = x_5 = \frac{w_b}{2} \qquad x_2 = x_6 = \frac{w_a}{2} \qquad x_3 = x_4 = \frac{w_a + w_b}{2}\]

那么最小割即为在提前拥有所有收益后必须付出的最小代价了

具体建图中,我们通常将边权乘\(2\)变为整数

回到这题,建图就很裸了

不过这题是不同产生收益,我们二分染色一下,然后对于其中一种颜色的点\(AB\)交换即可

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
using namespace std;
const int maxn = 10005,maxm = 200005,N = 105,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int A[N][N],B[N][N],C[N][N],id[N][N],X[4] = {0,0,-1,1},Y[4] = {-1,1,0,0};
int n,m,S,T;
int h[maxn],ne = 2;
struct EDGE{int to,nxt,f;}ed[maxm];
inline void build(int u,int v,int w){
    ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++;
    ed[ne] = (EDGE){u,h[v],0}; h[v] = ne++;
}
int d[maxn],vis[maxn],used[maxn],cur[maxn],now;
int q[maxn],head,tail;
inline bool bfs(){
    q[head = tail = 1] = S; vis[S] = now; d[S] = 0;
    int u;
    while (head <= tail){
        u = q[head++];
        Redge(u) if (ed[k].f && vis[to = ed[k].to] != now){
            d[to] = d[u] + 1; vis[to] = now;
            if (to == T) return true;
            q[++tail] = to;
        }
    }
    return vis[T] == now;
}
int dfs(int u,int minf){
    if (u == T || !minf) return minf;
    int flow = 0,f,to;
    if (used[u] != now) cur[u] = h[u],used[u] = now;
    for (int& k = cur[u]; k; k = ed[k].nxt)
        if (vis[to = ed[k].to] == now && d[to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
            ed[k].f -= f; ed[k ^ 1].f += f;
            flow += f; minf -= f;
            if (!minf) break;
        }
    return flow;
}
int maxflow(){
    int flow = 0; now = 1;
    while (bfs()){
        flow += dfs(S,INF);
        now++;
    }
    return flow;
}
int main(){
    n = read(); m = read(); S = 0; T = m * n + 1;
    int ans = 0;
    REP(i,n) REP(j,m) A[i][j] = read(),ans += A[i][j],A[i][j] <<= 1;
    REP(i,n) REP(j,m) B[i][j] = read(),ans += B[i][j],B[i][j] <<= 1;
    REP(i,n) REP(j,m) C[i][j] = read();
    REP(i,n) REP(j,m) id[i][j] = (i - 1) * m + j;
    REP(i,n) REP(j,m){
        int x,y,tmp;
        if ((i & 1) ^ (j & 1)){
            for (int k = 0; k < 4; k++){
                x = i + X[k];
                y = j + Y[k];
                if (x < 1 || y < 1 || x > n || y > m) continue;
                tmp = C[i][j] + C[x][y]; ans += tmp << 1;
                A[i][j] += tmp; B[i][j] += tmp;
                A[x][y] += tmp; B[x][y] += tmp;
                build(id[i][j],id[x][y],tmp << 1);
                build(id[x][y],id[i][j],tmp << 1);
            }
        }
    }
    REP(i,n) REP(j,m){
        if ((i & 1) ^ (j & 1)){
            build(S,id[i][j],A[i][j]);
            build(id[i][j],T,B[i][j]);
        }
        else {
            build(S,id[i][j],B[i][j]);
            build(id[i][j],T,A[i][j]);
        }
    }
    printf("%d\n",ans - (maxflow() >> 1));
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8974197.html

时间: 2024-10-12 13:06:34

BZOJ2132 圈地计划 【最小割】的相关文章

【BZOJ2132】圈地计划 最小割

[BZOJ2132]圈地计划 Description 最近房地产商GDOI(Group of Dumbbells Or Idiots)从NOI(Nuts Old Idiots)手中得到了一块开发土地.据了解,这块土地是一块矩形的区域,可以纵横划分为N×M块小区域.GDOI要求将这些区域分为商业区和工业区来开发.根据不同的地形环境,每块小区域建造商业区和工业区能取得不同的经济价值.更具体点,对于第i行第j列的区域,建造商业区将得到Aij收益,建造工业区将得到Bij收益.另外不同的区域连在一起可以得

BZOJ 2132 圈地计划 最小割

题目大意:给定一个m*n的矩阵,每个位置如果作为商业区或者工业区各有一个收益,如果相邻两块是不同的也会有一个收益,求最大收益 吐槽:住宅区呢- - 地理老师骗我们- - 普通的最小割建图会遇到一个问题: 割断两块之间的边收益为正,即代价为负 因此我们如果正常建最小割,那么两块之间的边权就会是负的 那么我们将这个矩阵黑白染色,将白格ST反向 这样割断两块之间的连边相当于两块选择了同一用途,代价为正 就可以正常跑了 #include <cstdio> #include <cstring>

【BZOJ2132】 圈地计划 最小割

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43201521"); } 题解: 水题,经典模型是两个在一块会损失,显然很好做. 这个同样很好做,就是黑白染色,然后某种颜色该连S集的连T,该连T的连S. 代码: #include <queue> #include <cstdio&g

bzoj2132圈地计划

bzoj2132圈地计划 题意: 一块土地可以纵横划分为N×M块小区域.于第i行第j列的区域,建造商业区将得到Aij收益,建造工业区将得到Bij收益.而如果区域(i,j)相邻(相邻是指两个格子有公共边)有K块(显然K不超过4)类型不同于(i,j)的区域,则这块区域能增加k×Cij收益.已知收益矩阵A,B,C,求收益最大值. 题解: 因为附加收益不是两两之间的,所以不用考虑除以2的问题.由于需要两块土地属性不同,所以对整个棋盘进行黑白染色.如果一块土地A为黑色,则s->A :c[A商] A->T

线性规划与网络流24题第2题 太空飞行计划 最小割

/** 题目: 线性规划与网络流24题第2题 太空飞行计划 最小割 链接:http://www.cogs.pro/cogs/problem/problem.php?pid=727 题意:lv 思路:最大点权独立集(点集中任意两个点没有边相连,且点权和最大)=点权总和-最小点权覆盖集. 将实验和仪器看做节点. 实验放在二分图的左边, s->x, cap = 实验利润. 仪器放在右边, x->t, cap = 仪器费用. 如果实验u的进行需要仪器v,u->v, cap = INF. ans

[BZOJ2132] 圈地计划

Time Limit: 2 Sec  Memory Limit: 256 MBSubmit: 1350  Solved: 637[Submit][Status][Discuss] Description 最近房地产商GDOI(Group of Dumbbells Or Idiots)从NOI(Nuts Old Idiots)手中得到了一块开发土地.据了解,这块土地是一块矩形的区域,可以纵横划分为N×M块小区域.GDOI要求将这些区域分为商业区和工业区来开发.根据不同的地形环境,每块小区域建造商业

关于一类最小割图的建法

具体就是bzoj3894文理文科,bzoj2127happiness,bzoj2132圈地计划. 一个图,每个点可以选择A或者B,然后选A是获得收益ai,选b是获得收益bi. 首先是万能方法,对于很多图都可以:一个集合内的点同时选A(或者B)可以获得某个收益ci,那么再建一个点,那个点连A流量为c的边,连集合内每个点一条maxlongint的边,这样就保证如果集合内有选B的,那么这条边一定蛮流(被割掉). bzoj3894就是这种方法,然后多年前写云的小P(还是小M)的牧场也是这种方法. 然后是

【bzoj2132】圈地计划 网络流最小割

题目描述 最近房地产商GDOI(Group of Dumbbells Or Idiots)从NOI(Nuts Old Idiots)手中得到了一块开发土地.据了解,这块土地是一块矩形的区域,可以纵横划分为N×M块小区域.GDOI要求将这些区域分为商业区和工业区来开发.根据不同的地形环境,每块小区域建造商业区和工业区能取得不同的经济价值.更具体点,对于第i行第j列的区域,建造商业区将得到Aij收益,建造工业区将得到Bij收益.另外不同的区域连在一起可以得到额外的收益,即如果区域(I,j)相邻(相邻

BZOJ 2132 圈地计划(最小割)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2132 题意:n*m的格子染色黑白,对于格子(i,j)染黑色则价值为A[i][j],白色为B[i][j].若一个格子四周不同颜色的有x个,则额外的价值为x*C[i][j].求最大价值. 思路:将格子黑白染色分成两个集合X和Y.S集合为X中的A和Y中的B,T为X中的B和Y中的A.相邻的连边为两个格子的C值之和.总权值减去最小割即是答案. struct node { int v,cap,ne