To_Heart—题解——BZOJ3894 文理分科
@
题目
给个链接
其实这道题算是一个比较经典的最小割问题了
题解
因为对于任何一个点来说,他不是选择文就是选择理,那么对于这种只有两个状态的问题很容易想到用最小割来限制,那么问题就转换成在图上删去最小的贡献使得该图不连通,然后答案就是总贡献减去最小割了。
但是我们发现当一个点的上下左右和他的状态相同时会产生额外的贡献,所以我们需要考虑如何在相邻两点状态不相同时删除这一部分的贡献。
对于每个点,我们只需要建两个虚点,分别处理上下左右和当前点都是文科/理科的情况。
以文科为例,如果当前点的上下左右有一个点和他不同,那么反映在图上就是这个点和汇点的边没有被割掉,这时我们会选择把从虚点到原点的边删掉,否则这个图就不满足不连通了。
tips:如果你去看看@C2022lihan的题解你会发现我们的图是相同的,因为他授权我使用的。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
const int INF=0x3f3f3f3f;
struct zz{
int u,w,id;
};
vector<zz> v[100005];
struct Dinic{
int dist[100005],be[100005];
int s,t;
void Add(int x,int y,int z){
int idx=v[x].size(),idy=v[y].size();
v[x].push_back((zz){y,z,idy});
v[y].push_back((zz){x,0,idx});
}
bool BFS(){
bool f=0;memset(dist,-1,sizeof dist);
queue<int> q;q.push(s);
dist[s]=be[s]=0;
while(!q.empty()){
int x=q.front();q.pop();
int siz=v[x].size();
for(int i=0;i<siz;i++){
int y=v[x][i].u,w=v[x][i].w;
if(!w||dist[y]!=-1) continue;
q.push(y),be[y]=0,dist[y]=dist[x]+1;
if(y==t) f=1;
}
}
return f;
}
int DFS(int x,int sum){
if(x==t||!sum) return sum;
int siz=v[x].size(),ans=0;
for(int i=be[x];i<siz;i++){
int y=v[x][i].u,w=v[x][i].w,id=v[x][i].id;be[x]=i;
if(!w||dist[x]!=dist[y]-1) continue;
int now=DFS(y,min(sum-ans,w));
if(!now) dist[y]=0;
v[x][i].w-=now,v[y][id].w+=now,ans+=now;
}
return ans;
}
int dinic(){
int ans=0,now=0;
while(BFS()) while(now=DFS(s,INF)) ans+=now;
// cout<<ans<<endl;
return ans;
}
}T;
int n,m,sum=0;
int a[105][105],b[105][105],aa[105][105],bb[105][105];
int ID(int x,int y){
return (x-1)*m+y;
}
int fx[5]={0,1,-1,0,0};
int fy[5]={0,0,0,1,-1};
bool Check(int x,int y){
if(x<=0||x>n) return 0;
if(y<=0||y>m) return 0;
return 1;
}
signed main(){
ios_base::sync_with_stdio(false);
cin.tie(NULL), std::cout.tie(NULL);
cin>>n>>m;T.s=0,T.t=n*m*3+1;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j];
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>b[i][j];
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>aa[i][j];
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>bb[i][j];
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) T.Add(T.s,ID(i,j),a[i][j]),T.Add(ID(i,j),T.t,b[i][j]),sum+=a[i][j]+b[i][j];
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
T.Add(T.s,ID(i,j)+n*m,aa[i][j]);T.Add(ID(i,j)+n*m*2,T.t,bb[i][j]);
sum+=aa[i][j]+bb[i][j];
for(int k=0;k<=4;k++){
int x=i+fx[k],y=j+fy[k];
if(!Check(x,y)) continue;
T.Add(ID(i,j)+n*m,ID(x,y),INF),T.Add(ID(x,y),ID(i,j)+n*m*2,INF);
}
}
printf("%lld\n",sum-T.dinic());
return 0;
}