3894: 文理分科
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1076 Solved: 630
[Submit][Status][Discuss]
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
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
思路{
首先应当想到这是一道最小割的题。
如何连边呢?我们想,
无论怎么样即使连一条Inf的边它也不会是最小割(因为有其他边的限制);
要保证最小割能分成两个点集,且保证决策的合理性,还要考虑周围的人的影响。
所以我们不难想到{
设源点S所在的点集为文科,汇点T所在点集为理科.
那么连S到点i的容量为文科费用,i到T的容量为理科费用。
好的,接下来是最重要的一部分。
一个点和周围4个点有共同贡献,所以考虑新建一个点;
与这5个点连Inf的边,贡献的话,
由于是割,最后的答案为总权值减最小割,那么
文科贡献连S∈割集就对应当前这些点不属于文科,理科同理。
否则的话连出来无意义。。。。
}还是比较水的,还有一道一毛一样的题BZOJ2127happiness
网络流好久没敲了,调了好久。。。。。。。。
}
#include<bits/stdc++.h> #define N 101*101 #define Inf (1<<20) #define pos(i,j) ((i-1)*m+j) using namespace std; struct ed{int nxt,to,c;}e[N*100]; int head[N*10],tot,deep[N*10],n,m; void add(int u,int v,int c){e[tot].nxt=head[u];e[tot].to=v;e[tot].c=c;head[u]=tot++;} void ADD(int u,int v,int c){add(u,v,c),add(v,u,0);} bool BFS(int s,int t){queue<int>que;while(!que.empty())que.pop(); memset(deep,0,sizeof(deep));deep[s]=1; que.push(s);while(!que.empty()){ int u=que.front();que.pop(); for(int i=head[u];i!=-1;i=e[i].nxt)if(e[i].c&&(!deep[e[i].to])){ int v=e[i].to;deep[v]=deep[u]+1; que.push(v); if(v==t)return true; } }return false; } int dinic(int s,int t,int T){ if(s==t)return T;int tag(0); for(int i=head[s];i!=-1;i=e[i].nxt)if(deep[e[i].to]==deep[s]+1&&e[i].c){ int v=e[i].to;int d=dinic(v,t,min(T-tag,e[i].c)); e[i].c-=d,e[i^1].c+=d;tag+=d; if(tag==T)break; }if(!tag)deep[s]=0;return tag; } int maxflow(int s,int t){int flow=0; while(BFS(s,t))flow+=dinic(s,t,Inf); return flow; } int main(){ freopen("1.in","r",stdin); //freopen("1.out","w",stdout); memset(head,-1,sizeof(head)); int f,Ans(0);scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",&f),ADD(0,pos(i,j),f),Ans+=f; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",&f),ADD(pos(i,j),n*m+1,f),Ans+=f; int cnt=n*m+1; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ scanf("%d",&f);cnt++; if(i!=1)ADD(cnt,pos(i-1,j),Inf); if(i!=n)ADD(cnt,pos(i+1,j),Inf); if(j!=1)ADD(cnt,pos(i,j-1),Inf); if(j!=m)ADD(cnt,pos(i,j+1),Inf); ADD(cnt,pos(i,j),Inf); ADD(0,cnt,f),Ans+=f; } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ scanf("%d",&f);cnt++; if(i!=1)ADD(pos(i-1,j),cnt,Inf); if(i!=n)ADD(pos(i+1,j),cnt,Inf); if(j!=1)ADD(pos(i,j-1),cnt,Inf); if(j!=m)ADD(pos(i,j+1),cnt,Inf); ADD(pos(i,j),cnt,Inf); ADD(cnt,n*m+1,f),Ans+=f; } cout<<Ans-maxflow(0,n*m+1)<<" "; return 0; }