【问题描述】
高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。作为计算机竞赛教练的scp大老板,想知道如何分配可以使得全班的喜悦值总和最大。
【输入格式】
第一行两个正整数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列的同学同时选择理科获得的额外喜悦值。
【输出格式】
输出一个整数,表示喜悦值总和的最大值
【样例输入】
1 2
1 1
100 110
1
1000
【样例输出】
1210
【样例说明】
两人都选理,则获得100+110+1000的喜悦值。
【数据规模和约定】
对于10%以内的数据,n,m<=4
对于30%以内的数据,n,m<=8
对于100%以内的数据,n,m<=100 数据保证答案在2^30以内
对于100%的数据,时间限制为0.5s。
思路:网上主要流传的是解方程的方式,我用了一种求最小割的方式。
首先,考虑最小割就是考虑最小的损失,那么转化一下模型。考虑损失。设置源点为文,汇点为理,那么每个点向源点汇点各连一条边权为喜悦值的边。然后两个点之间有关系就在设置一个点,两个点向这个点连边权为正无穷的边,然后有这个点向源点或汇点连一条边权为两点同时选文或同时选理的喜悦值。然后跑dinic求最小割,如果两个点一个选文一个选理,那么就表示割掉同时选文同时选理两条边,同时选文,则割掉同时选理,反之同理,那么求最小割就表示保证最小的损失。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 1e9
#define MAXN 100005
using namespace std;
queue<int> q;
struct node
{
int from,to,remain,next;
}e[MAXN*50];
int e_num=-1,point[MAXN];
int deep[MAXN],cur[MAXN];
bool vis[MAXN];
int s,t,ans,n,m;
void add(int from,int to,int remain)
{
++e_num;
e[e_num].from=from;
e[e_num].to=to;
e[e_num].remain=remain;
e[e_num].next=point[from];
point[from]=e_num;
}
bool bfs(int from,int to)
{
memset(deep,0x7f,sizeof(deep));
memset(vis,0,sizeof(vis));
for (int i=0;i<=t+10;i++)
cur[i]=point[i];
deep[from]=0;
vis[from]=1;
q.push(from);
while (!q.empty())
{
int u=q.front();
q.pop();
vis[u]=false;
for (int i=point[u];i!=-1;i=e[i].next)
if (deep[e[i].to]>inf&&e[i].remain)
{
deep[e[i].to]=deep[u]+1;
if (!vis[e[i].to])
{
q.push(e[i].to);
vis[e[i].to]=true;
}
}
}
return deep[to]<inf;
}
int dfs(int from,int to,int limit)
{
int f,flow=0;
if (!limit) return 0;
if (from==to) return limit;
for (int i=cur[from];i!=-1;i=e[i].next)
{
cur[from]=i;
if (deep[e[i].to]==deep[from]+1&&(f=dfs(e[i].to,to,min(limit,e[i].remain))))
{
flow+=f;
limit-=f;
e[i].remain-=f;
e[i^1].remain+=f;
if (!limit) break;
}
}
return flow;
}
void dinic()
{
while (bfs(s,t))
ans+=dfs(s,t,inf);
}
void addedge(int x,int y,int z)
{
add(x,y,z);
add(y,x,0);
}
int main()
{
freopen("nt2011_happiness.in","r",stdin);
freopen("nt2011_happiness.out","w",stdout);
int x,sum=0;
memset(point,-1,sizeof(point));
scanf("%d%d",&n,&m);
s=0;t=n*m*5+100;
for (int i=1;i<=n;i++)//文
for (int j=1;j<=m;j++)
{
scanf("%d",&x);
sum+=x;
addedge(s,(i-1)*m+j,x);
}
for (int i=1;i<=n;i++) //理
for (int j=1;j<=m;j++)
{
scanf("%d",&x);
sum+=x;
addedge((i-1)*m+j,t,x);
}
int po=n*m;
for (int i=1;i<=n-1;i++) //文
for (int j=1;j<=m;j++)
{
scanf("%d",&x);
sum+=x;po++;
addedge(po,(i-1)*m+j,inf);
addedge(po,i*m+j,inf);
addedge(s,po,x);
}
for (int i=1;i<=n-1;i++) //理
for (int j=1;j<=m;j++)
{
scanf("%d",&x);
sum+=x;po++;
addedge((i-1)*m+j,po,inf);
addedge(i*m+j,po,inf);
addedge(po,t,x);
}
for (int i=1;i<=n;i++) //文
for (int j=1;j<=m-1;j++)
{
scanf("%d",&x);
sum+=x;po++;
addedge(po,(i-1)*m+j,inf);
addedge(po,(i-1)*m+1+j,inf);
addedge(s,po,x);
}
for (int i=1;i<=n;i++) //理
for (int j=1;j<=m-1;j++)
{
scanf("%d",&x);
sum+=x;po++;
addedge((i-1)*m+j,po,inf);
addedge((i-1)*m+1+j,po,inf);
addedge(po,t,x);
}
dinic();
printf("%d",sum-ans);
}