题目大意:
求网格图的最小割。
题解:
把平面图中的每一个面积块转化为一个结点,并在网格外的面积块中分出两个面积块分别作为S和T,spfa求出的最短路即为最小割。
因为只要选择了一条联通的S-T路径,图一定被割为两部分。
update!
把图建好之后应该是这样的,每条新边(彩色边)的边权就是它割开的原图中那条边的边权。
如果选择了一条从S到T的路径(如图中浅绿色路径所示),那么就相当于选择了一些原图中的边并将其割掉(图中深绿色的边)。但是图中的路径并不一定是最短的。
已知这样的一个割一定能将start和end分开,所以只要选择一个最小的这样的割就可以了,也就是说选择一条最短的S-T路径就是它的最小割。
代码:
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
const int maxn=int(2e3)+111, maxd=int(2e6)+111, maxe=int(4e6);
int n,m;
int S,T;
#define pos(x,y) (S+((x)-1)*m+(y))
int tot=0,head[maxd];
struct Edge {
int from,to,val,next;
Edge() {}
Edge(int x,int y,int v,int nx):from(x),to(y),val(v),next(nx) {}
}eage[maxe*2];
void add(int x,int y,int val) {
eage[tot]=Edge(x,y,val,head[x]), head[x]=tot++;
eage[tot]=Edge(y,x,val,head[y]), head[y]=tot++;
return;
}
const long long INF=(1ll<<62);
queue<int> que;
long long dis[maxd];
bool used[maxd];
int main() {
#ifndef ONLINE_JUDGE
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE
scanf("%d%d",&n,&m);
n=n-1, m=2*(m-1);
S=1, T=pos(n,m)+1;
for(int i=S;i<=T;i++) head[i]=-1;
for(int i=1;i<=n+1;i++)
for(int j=1;j<=m/2;j++) {
int val;
scanf("%d",&val);
if(i==1) add(pos(1,j*2),T,val);
else if(i<=n) add(pos(i-1,j*2-1),pos(i,j*2),val);
else add(S,pos(n,j*2-1),val);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m/2+1;j++) {
int val;
scanf("%d",&val);
if(j==1) add(S,pos(i,j*2-1),val);
else if(j<=m/2) add(pos(i,(j-1)*2),pos(i,j*2-1),val);
else add(pos(i,m),T,val);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m/2;j++) {
int val;
scanf("%d",&val);
add(pos(i,j*2-1),pos(i,j*2),val);
}
for(int i=S;i<=T;i++)
dis[i]=INF, used[i]=false;
que.push(S);
dis[S]=0;
used[S]=true;
while(que.size()) {
int u=que.front(); que.pop();
used[u]=false;
for(int i=head[u];~i;i=eage[i].next) if(dis[eage[i].to]>dis[u]+eage[i].val) {
int v=eage[i].to;
dis[v]=dis[u]+eage[i].val;
if(!used[v]) {used[v]=true; que.push(v);}
}
}
printf("%lld\n",dis[T]);
return 0;
}