BZOJ3894文理分科

3894: 文理分科
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 397 Solved: 244
Description
文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠
结过)
小P所在的班级要进行文理分科。他的班级可以用一个n*m的矩阵进行
描述,每个格子代表一个同学的座位。每位同学必须从文科和理科中选择
一科。同学们在选择科目的时候会获得一个满意值。满意值按如下的方式
得到:
1.如果第i行第秒J的同学选择了文科,则他将获得art[i][j]的满意值,如
果选择理科,将得到science[i][j]的满意值。
2.如果第i行第J列的同学选择了文科,并且他相邻(两个格子相邻当且
仅当它们拥有一条相同的边)的同学全部选择了文科,则他会更开
心,所以会增加same_art[i][j]的满意值。
3.如果第i行第j列的同学选择了理科,并且他相邻的同学全部选择了理
科,则增加same_science[i]j[]的满意值。
小P想知道,大家应该如何选择,才能使所有人的满意值之和最大。请
告诉他这个最大值。
Input
第一行为两个正整数:n,m
接下来n术m个整数,表示art[i][j];
接下来n术m个整数.表示science[i][j];
接下来n术m个整数,表示same_art[i][j];
Output
输出为一个整数,表示最大的满意值之和
Sample Input
3 4
13 2 4 13
7 13 8 12
18 17 0 5
8 13 15 4
11 3 8 11
11 18 6 5
1 2 3 4
4 2 3 2
3 1 0 4
3 2 3 2
0 2 2 1
0 2 4 4
Sample Output
152
HINT
样例说明
1表示选择文科,0表示选择理科,方案如下:
1 0 0 1
0 1 0 0
1 0 0 0
N,M<=100,读入数据均<=500
最小割经典模型。。
先求出总收益,用总收益-最大流就是答案。
建图方式:
把文理科分属于S集和T集,前两个矩阵分别转点,然后源点向矩阵转点连边,流量为矩阵点权,另一个向汇点连边,同理,对于选择相同科的获得的满意度,对于每个点,增设两个辅助点,一个向源点连流量为相同科满意度的边,向集合内的每个点连边,流量为inf,另一个辅助点向汇点连流量为相同科满意度的边,向集合内的每个点连边,流量为inf。
附上本蒟蒻的代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define MAXN 100001
#define inf 0x7fffffff
int n,m,cnt=1,head,tail,ans=0,sum,h[MAXN],q[MAXN],dis[MAXN],T,row[5]={0,0,1,-1,0},line[5]={1,-1,0,0,0};
struct kx
{
    int to,next,v;
}edge[MAXN*10];

int read()
{
    int w=0,c=1; char ch=getchar();
    while (ch<'0' || ch>'9')
      {
        if (ch=='-') c=-1;
        ch=getchar();
      }
    while (ch>='0' && ch<='9')
      w=w*10+ch-'0',ch=getchar();
    return w*c;
}

int get(int x,int y)
{
    return (x-1)*m+y;
}

void add(int u,int v,int w)
{
    cnt++,edge[cnt].next=h[u],h[u]=cnt,edge[cnt].to=v,edge[cnt].v=w;
    cnt++,edge[cnt].next=h[v],h[v]=cnt,edge[cnt].to=u,edge[cnt].v=0;
}

bool bfs()
{
    int j,p;
    memset(dis,-1,sizeof(dis));
    head=0,tail=1,dis[0]=q[0]=0;
    while (head<tail)
      {
        head++,j=q[head],p=h[j];
        while (p)
          {
            if (edge[p].v>0 && dis[edge[p].to]<0)
              dis[edge[p].to]=dis[j]+1,tail++,q[tail]=edge[p].to;
            p=edge[p].next;
          }
      }
    if (dis[T]>0) return true;
    else return false;
}

int dfs(int x,int f)
{
    int w,used=0,i=h[x];
    if (x==T) return f;
    while (i)
      {
        if (dis[edge[i].to]==dis[x]+1 && edge[i].v)
          {
            w=f-used,w=dfs(edge[i].to,min(w,edge[i].v));
            edge[i].v-=w,edge[i^1].v+=w,used+=w;
            if (used==f) return f;
          }
        i=edge[i].next;
      }
    if (!used) dis[x]=-1;
    return used;
}

int main()
{
    int i,j,k,x,y,value;
    n=read(),m=read(),T=3*n*m+1;
    for (i=1;i<=n;i++)
      for (j=1;j<=m;j++)
        {
            x=read(),ans+=x;
            add(0,get(i,j),x);
        }
    for (i=1;i<=n;i++)
      for (j=1;j<=m;j++)
        {
            x=read(),ans+=x;
            add(get(i,j),T,x);
        }
    for (i=1;i<=n;i++)
      for (j=1;j<=m;j++)
        {
            value=read(),ans+=value;
            for (k=0;k<5;k++)
              {
                x=row[k]+i,y=line[k]+j;
                if (x>n || y>m || x<1 || y<1) continue;
                add(get(i,j)+n*m,get(x,y),inf);
              }
            add(0,get(i,j)+n*m,value);
        }
    for (i=1;i<=n;i++)
      for (j=1;j<=m;j++)
        {
            value=read(),ans+=value;
            for (k=0;k<5;k++)
              {
                x=row[k]+i,y=line[k]+j;
                if (x>n || y>m || x<1 || y<1) continue;
                add(get(x,y),get(i,j)+2*n*m,inf);
              }
            add(get(i,j)+2*n*m,T,value);
        }
    while (bfs())
      while (sum=dfs(0,inf))
        ans-=sum;
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值