题目大概说给一个n×n的方格,边有权值,问从求(1,1)到(n,n)的最小割。
点达到了160000个,直接最大流不好。这题的图是平面图,求最小割可以转化成求其对偶图的最短路,来更高效地求解:
首先源点汇点间新加一条边,然后构造其对偶图:
- 面作为对偶图的点;而源点到汇点之间新加的边划分出来的两个面分别作为对偶图的源点和汇点
- 如果两个面之间有边则两个面在对偶图对应的点连边,权值为原来的边权;去掉对偶图源点和汇点之间边
这样可以发现,对偶图的源点到汇点的一条路径就对应这原图的源点到汇点的一个割边集,而最短路就对应最小割了。所以求一下最小割就OK了,我用SPFA好像超时了,改用堆优化的Dijkstra,10W个点OK。
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define INF (1<<30)
#define MAXN 1111*1111
struct Edge{
int v,w,next;
}edge[MAXN<<];
int vs,vt,NV,NE,head[MAXN];
void addEdge(int u,int v,int w){
edge[NE].v=v; edge[NE].w=w; edge[NE].next=head[u];
head[u]=NE++;
}
struct Node{
int u,d;
Node(int _u=,int _d=):u(_u),d(_d){}
bool operator<(const Node &nd)const{
return nd.d<d;
}
};
int d[MAXN];
bool vis[MAXN];
int dijkstra(){
for(int i=; i<NV; ++i){
d[i]=INF; vis[i]=;
}
d[vs]=;
priority_queue<Node> que;
que.push(Node(vs,));
while(!que.empty()){
Node nd=que.top(); que.pop();
if(nd.u==vt) return nd.d;
if(vis[nd.u]) continue;
vis[nd.u]=;
for(int i=head[nd.u]; i!=-; i=edge[i].next){
int v=edge[i].v;
if(vis[v]) continue;
if(d[v]>d[nd.u]+edge[i].w){
d[v]=d[nd.u]+edge[i].w;
que.push(Node(v,d[v]));
}
}
}
return INF;
}
int a[][];
int main(){
int t,n;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
if(n==){
puts("");
continue;
}
vs=(n-)*(n-); vt=vs+; NV=vt+; NE=;
memset(head,-,sizeof(head));
for(int i=; i<n; ++i){
for(int j=; j<n; ++j){
scanf("%d",&a[i][j]);
}
}
for(int i=; i<n-; ++i){
for(int j=; j<n; ++j){
if(j==){
addEdge(vs,i*(n-)+j,a[i][j]);
}else if(j==n-){
addEdge(i*(n-)+j-,vt,a[i][j]);
}else{
addEdge(i*(n-)+j,i*(n-)+j-,a[i][j]);
addEdge(i*(n-)+j-,i*(n-)+j,a[i][j]);
}
}
}
for(int j=; j<n-; ++j){
for(int i=; i<n; ++i){
if(i==){
addEdge(i*(n-)+j,vt,a[i][j]);
}else if(i==n-){
addEdge(vs,(i-)*(n-)+j,a[i][j]);
}else{
addEdge(i*(n-)+j,(i-)*(n-)+j,a[i][j]);
addEdge((i-)*(n-)+j,i*(n-)+j,a[i][j]);
}
}
}
printf("%d\n",dijkstra());
}
return ;
}