【2011集训队出题】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

Data Constraint

Hint

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

题解

这是一道二元关系的题目
这一道题目要求的是最大值,所以我们不妨用所有值的和减去最小割
这里写图片描述
然后就列出一堆式子然后解方程
具体的就不说了
其中有一个比较坑的地方,就是其实是没有确定解的以为方程不够,但是我们考虑到比如x和S的连边和y选文选理没有关系,这样就可以解出来我们想要的那个解了

然后跑一波最大流就好了

贴代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)

using namespace std;

const int maxn=1e5+5;

int fi[maxn],ne[maxn*2],dui[maxn*2],dui1[maxn*2],qc[maxn];
int mt[maxn],a[105][105][8],p[105][105];
int h[maxn],de[10010];
int i,j,k,l,m,n,x,y,tot,ans,now,cp,c1,c2;

void add(int x,int y,int z){
    if (fi[x]==0) fi[x]=++now; else ne[qc[x]]=++now;
    dui[now]=y; dui1[now]=z; qc[x]=now;
}
bool bfs(){
    memset(de,255,sizeof(de)); de[cp+1]=0;
    i=1; j=0; h[1]=cp+1;
    while (i>j){
        x=h[++j]; k=fi[x];
        while (k){
            if (dui1[k] && de[dui[k]]==-1){
                de[dui[k]]=de[x]+1; h[++i]=dui[k];
            }
            k=ne[k];
        }
    }
    if (de[cp+2]==-1) return false; else return true;
}


int dinic(int x,int w){
    int i=fi[x],p,now=0;
    if (x==cp+2) return w;
    while (i){
        if (de[dui[i]]==de[x]+1 && dui1[i]>0){
            p=dinic(dui[i],min(w-now,dui1[i]));
            if (p){
                dui1[i]-=p;
                dui1[i xor 1]+=p;
                now+=p;
            }
        }
        i=ne[i];
    }
    return now;
}
int main(){
    freopen("1919.in","r",stdin);
    scanf("%d%d",&n,&m);
    fo(i,1,n) fo(j,1,m) p[i][j]=++cp;
    fo(i,1,n) fo(j,1,m) scanf("%d",&a[i][j][1]);
    fo(i,1,n) fo(j,1,m) scanf("%d",&a[i][j][2]);
    fo(i,1,n-1) fo(j,1,m) scanf("%d",&a[i][j][3]);
    fo(i,1,n-1) fo(j,1,m) scanf("%d",&a[i][j][4]);
    fo(i,1,n) fo(j,1,m-1) scanf("%d",&a[i][j][5]);
    fo(i,1,n) fo(j,1,m-1) scanf("%d",&a[i][j][6]);
    fo(k,1,6) fo(i,1,n) fo(j,1,m){
        a[i][j][k]=a[i][j][k]*2;
        tot=tot+a[i][j][k];
    }
    now=1;
    fo(i,3,n) fo(j,2,m){
        x=p[i][j]; c1=c2=0;
        c1=a[i][j][4]+a[i][j][6]+a[i-1][j][4]+a[i][j-1][6];
        c2=a[i][j][3]+a[i][j][5]+a[i-1][j][3]+a[i][j-1][5];
        add(cp+1,x,(c2/2)+a[i][j][1]); add(x,cp+1,0);
        add(x,cp+2,(c1/2)+a[i][j][2]); add(cp+2,x,0);
        if (i!=n){
            y=p[i+1][j]; c1=(a[i][j][3]+a[i][j][4])/2;
            add(x,y,c1); add(y,x,c1);
        }

        if (j!=m){
            y=p[i][j+1]; c1=(a[i][j][5]+a[i][j][6])/2;
            add(x,y,c1); add(y,x,c1);
        }

    }
    while (bfs()){
        l=dinic(cp+1,6666666);
        ans+=l;
    }
    printf("%d\n",(tot-ans)/2);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值