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
Hint
【样例说明】
两人都选理,则获得100+110+1000的喜悦值。
【数据规模】
对于10%以内的数据,n,m<=4
对于30%以内的数据,n,m<=8
对于100%以内的数据,n,m<=100 所有喜悦值均为小于等于5000的非负整数
Solution
这一题的建模比较有创意。
考虑建立一个超级源点S和一个超级汇点T。
从S连边到某个点表示选文科,从某个点连边到T表示选理科,边权值为给定的喜悦值。
对于两个点都选文的情况,就新建一个中间点,从S连到中间点,边权为喜悦值,再从中间点连向这两个点,边权设为inf。
类似的可以处理处理科的情况。
这里的连边很关键,也不是很好想。
代码还是比较好懂的。
Code
#include <cstdio>
#include <algorithm>
#include <cstring>
#define P(x,y) (~-(x)*m+(y)) //快速算点坐标
#define fo(i,a,b) for (i=a;i<=b;i++)
using namespace std;
const int maxn=500000;
const int inf=10000007;
int n,m,i,j,p,x,tot,ans,s,t,cnt;
int next[maxn],head[maxn],b[maxn],c[maxn],d[maxn],q[maxn],cur[maxn];
int read(){
int su=0;
char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') {
su=su*10+c-'0';
c=getchar();
}
return su;
}
void plus(int x,int y,int z){ next[++tot]=head[x]; head[x]=tot; b[tot]=y; c[tot]=z;}
void add(int x,int y,int z){ plus(x,y,z); plus(y,x,0);}
int bfs(){
int l,r,x,i;
l=0,r=1; q[r]=s;
memset(d,0,sizeof(d));
d[s]=1;
while (l<r) {
x=q[++l];
for (i=head[x];i!=-1;i=next[i]){
if (c[i]!=0&&d[b[i]]==0) {
q[++r]=b[i];
d[b[i]]=d[x]+1;
}
}
}
if (d[t]>0) return 1;
else return 0;
}
int dfs(int x,int f){
if (x==t) return f;
for (int & i=cur[x];i!=-1;i=next[i]){
if ((d[b[i]]==d[x]+1)&&(c[i]!=0)){
int di=dfs(b[i],min(c[i],f));
if (di>0) {
c[i]-=di;c[i^1]+=di;return di;}
}
}
return 0;
}
int main(){
n=read(),m=read();
tot=-1,cnt=n*m;
memset(head,-1,sizeof(head));
memset(next,-1,sizeof(next));
s=0; t=6*n*m+1;
int sum;
fo(i,1,n)
fo(j,1,m) {
x=read();
sum+=x;
p=P(i,j);
add(s,p,x);
}
fo(i,1,n)
fo(j,1,m) {
x=read();
sum+=x;
p=P(i,j);
add(p,t,x);
}
fo(i,1,n-1)
fo(j,1,m) {
x=read();
sum+=x;
p=P(i,j); cnt++;
add(s,cnt,x); add(cnt,p,inf); add(cnt,p+m,inf);
}
fo(i,1,n-1)
fo(j,1,m) {
x=read();
sum+=x;
p=P(i,j); cnt++;
add(cnt,t,x); add(p,cnt,inf); add(p+m,cnt,inf);
}
fo(i,1,n)
fo(j,1,m-1) {
x=read();
sum+=x;
p=P(i,j); cnt++;
add(s,cnt,x); add(cnt,p,inf); add(cnt,p+1,inf);
}
fo(i,1,n)
fo(j,1,m-1){
x=read();
sum+=x;
p=P(i,j); cnt++;
add(cnt,t,x); add(p,cnt,inf); add(p+1,cnt,inf);
}
while (bfs()) {
fo(i,s,t) cur[i]=head[i];
while (int di=dfs(s,inf)) ans+=di;
}
printf("%d\n",sum-ans);
}