bzoj2127: happiness 浅析一类最小割问题——二元关系的应用

bzoj2127: happiness

Description

高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。作为计算机竞赛教练的scp大老板,想知道如何分配可以使得全班的喜悦值总和最大。

Input

第一行两个正整数n,m。接下来是六个矩阵第一个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学选择文科获得的喜悦值。第二个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学选择理科获得的喜悦值。第三个矩阵为n-1行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i+1行第j列的同学同时选择文科获得的额外喜悦值。第四个矩阵为n-1行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i+1行第j列的同学同时选择理科获得的额外喜悦值。第五个矩阵为n行m-1列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i行第j+1列的同学同时选择文科获得的额外喜悦值。第六个矩阵为n行m-1列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i行第j+1列的同学同时选择理科获得的额外喜悦值。

Output

输出一个整数,表示喜悦值总和的最大值

Sample Input

1 2
1 1
100 110
1
1000

Sample Output

1210
【样例说明】
两人都选理,则获得100+110+1000的喜悦值。
【数据规模】
对于100%以内的数据,n,m<=100 所有喜悦值均为小于等于5000的非负整数

分析

这是一道很神的最小割的题目。
看过彭天翼的《浅析一类最小割问题》的人做这道应该不难。
不过鉴于网上这个论文实在少,我简单说明一下建图的模型。

问题一般化

有N个任务,每个任务可以在A上或者B上完成,花费分别为 ai bi 对于每一对二元关系(x,y)有如下要求

1.x,y同时在A上完成,花费为 v1
2.x,y同时在B上完成,花费为 v2
3.x在A上完成,y在B上完成,花费为 v3
4.x在B上完成,y在A上完成,花费为 v4

建模


(图很劣质,请勿吐槽)
我们考虑最小割,如果S与x的边被割(x被割到T集)中表示在A机器上完成,如果x与T的边被割(x被割到S集)中表示在B机器上完成。
那么于是,我们可以列出以下方程组。
a+b=v1 ……①
c+d=v2 ……②
a+d+f=v3 ……③
b+c+e=v4 ……④
我们用③+④-①-②得到
f+e=v3+v4v1v2
我们令 k=v3+v4v1v2
我们要求最小割每条边都必须大于零,但是由于 a,c 我们选择且仅选择一个, b,d 也是如此,我们可以把 a,b,c,d 同时加上一些值让它们大于零,于是唯一的要求就是 k>0
那么如果 k>0 ,那么为了方便一般设 e=f=k2 然后我们在随便确定 a,b,c,d 中一个数的值即可解出方程。
那么 k<0 怎么办?
我们考虑改变xy建边原有的含义

(差不多就好了)
我们让S到x边被割表示x在B机器上工作,x到T上的边被割表示在A机器上工作
重新写方程
a+b=v3 ……①
c+d=v4 ……②
a+d+f=v1 ……③
b+c+e=v2 ……④
这个时候K’=-K我们解决了K<0的问题
但是注意,如果要是(x,y)的K<0,(x,z)的K>0,这个时候这个模型就不在适用了,因为x与S的边被割到只能表示一种意义。

题目分析

差不多抄完了彭天翼的论文,下面开始做这道题。
首先肯定最大转最小,我们考虑一对朋友不能同时选文或选理的”悲伤值”。
然后我们考虑好朋友二元关系(x,y)
我们假设 v1,v2 分别表示同时选文科,同时选理科的喜悦值
那么有
a+b=v2 ……①
c+d=v1 ……②
a+d+f=v1+v2 ……③
b+c+e=v1+v2 ……④
K=v1+v2>0
我们令 a=b=v22
最后解得
e=f=K2=(v1+v2)2a=b=v22,c=d=v12
挺优美的建边方式,不过美中不足的是有小数,那么我们把所有权值乘二,统计答案的时候在除回来即可。

代码

比较古老的代码,勉强看看吧

/**************************************************************
    Problem: 2127
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:720 ms
    Memory:17700 kb
****************************************************************/

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cstdlib>
#define inf 0x7fffffff
using namespace std;
int m, n, mans; //problem
int art[3][100001], sci[3][100001];

int gap[100001],h[100001], s, t; //sap

int read() //fast io
{
    char ch = getchar();int x = 0, f = 1;
    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;
}

struct edge
{
    int to, next, w;
}e[1000001];int pre[100001], cur[100001], top, d[100001], p[100001]; //graph

void add(int u, int v, int w)
{
    e[top].next = pre[u];e[top].w = w;
    e[top].to = v;pre[u] = top++;
    e[top].next = pre[v];e[top].w = 0;
    e[top].to = u;pre[v] = top++;
}

int position(int i, int j)
{
    if(i < 1 || i > n || j < 1 || j > m) return 0;
    return (i - 1) * m + j;
}

void get_matrix(int *a, int n, int m)
{
    for(int i = 1;i <= n; ++i)
        for(int j = 1;j <= m; ++j)
        {
            int w = read();mans += w;
            a[position(i, j)] = w;
        }
}

void get_matrix_as(int i, int n, int m)
{
    get_matrix(art[i], n, m);
    get_matrix(sci[i], n, m);
}

void get_graph()
{
    memset(pre, -1, sizeof(pre)); top = 0;
    s = 0; t = n * m + 1;
    for(int i = 1;i <= n; ++i)
        for(int j = 1;j <= m; ++j)
        {
            int cur = position(i, j), up = position(i - 1, j);
            int down = position(i + 1, j), left = position(i, j - 1);
            int right = position(i, j + 1);
            int s_w = sci[2][left] + sci[2][cur] + sci[1][up] + sci[1][cur];
            s_w += (sci[0][cur] << 1);
            add(s, cur, s_w); 
            int t_w = art[2][left] + art[2][cur] + art[1][up] + art[1][cur];
            t_w += (art[0][cur] << 1);
            add(cur, t, t_w);
            if(up) add(cur, up, art[1][up] + sci[1][up]);
            if(down) add(cur, down, art[1][cur] + sci[1][cur]);
            if(left) add(cur, left, art[2][left] + sci[2][left]);
            if(right) add(cur, right, art[2][cur] + sci[2][cur]);
        }
}

void init()
{
    n = read(); m = read();
    get_matrix_as(0, n, m);
    get_matrix_as(1, n - 1, m);
    get_matrix_as(2, n, m - 1);
    mans <<= 1;
    get_graph();
}

void bfs()
{
    queue<int>q;
    memset(h, -1, sizeof(h));
    memset(gap, 0, sizeof(gap));
    gap[++h[t]]++;
    q.push(t);
    while(!q.empty())
    {
        int u = q.front();q.pop();
        for(int i = pre[u]; ~i; i = e[i].next)
        {
            int v = e[i].to;
            if(!(~h[v]))
            {
                gap[h[v] = h[u] + 1]++;
                q.push(v);
            }
        }
    }
    if(!(~h[s])) h[s] = t + 2;
}

int dfs(int u, int minf)
{
    if(u == t) return minf;
    int flow = 0, f;
    for(int i = cur[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(h[u] == h[v] + 1 && e[i].w > 0)
        {
            f = min(e[i].w, minf - flow);
            f = dfs(v, f);
            flow += f;
            e[i].w -= f;
            e[i ^ 1].w +=f;
            if(e[i].w > 0) cur[u] = i;
            if(flow == minf) return flow;
        }
    }
    if(!(--gap[h[u]])) h[s] = t + 2;
    gap[++h[u]]++;
    cur[u] = pre[u];
    return flow;
}

int sap()
{
    int ans = 0;
    bfs();
    for(int i = s;i <= t; ++i) cur[i] = pre[i];
    while(h[s] < t + 2) ans += dfs(s, inf);
    return (mans - ans) >> 1;
}

int main()
{
    init();
    printf("%d\n",sap());
    return 0;
}
/*
4 4
5000 511 45 46
470 486 844 507
512 330 7 563
554 420 440 5000
560 505 211 5000
302 65 867 993
896 401 376 144
5000 681 468 529
748 975 769 737
219 524 374 962
775 286 810 834
339 927 93 203
694 939 626 429
521 873 773 692
513 850 809
272 367 970
489 220 356
618 943 868
732 411 670
438 229 466
928 622 969
908 693 844
*/
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页