题意:N*N的方格,每条边有权值,求从(1,1)到(n,n)的最小割。n<=400
这题是在S-T平面图中将最小割转化为最短路的题,推荐08年OI论文周冬《两极相通——浅析最大—最小定理在信息学竞赛中的应用》,没看论文的压力很大,果断不会。研究了一会,下面说下自己的理解。
何为S-T平面图:首先是一平面图(满足欧拉公式与存在对偶图),且源点S,汇点T在边界上。
如何构造对偶图:将S-T连线,将最外面的一个大面(无限大)一分为二了,一个为S*,一个为T*,然后将跨跃某条边的两个面连线,再去掉S*与T*的连线。从下图就可看出S*到T*的一条路径就会对应一个S至T割,S*到T*的最短路就对应最小割了,然后求S*到T*的最短路即可。如图
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxn=170000;
struct point
{
int v,w;
}p0;
vector<point> e[maxn];
//queue<int>q;//
int n,S,T,q[maxn];
int dist[maxn];
bool inq[maxn];
int SPFA()
{
int i,k,head=0,tail=0;
memset(inq,false,sizeof(inq));
for(i=0;i<=(n-1)*(n-1)+1;i++)
dist[i]=0x3fffffff;
// while(!q.empty()) q.pop();
// q.push(S);
q[tail++]=S;
dist[S]=0;
inq[S]=true;
while(head!=tail)
{
// k=q.front();
// q.pop();
k=q[head];
head=(head+1)%maxn;
inq[k]=false;
for(i=0;i<e[k].size();i++)
{
p0=e[k][i];
if(dist[p0.v]>dist[k]+p0.w)
{
dist[p0.v]=dist[k]+p0.w;
if(!inq[p0.v])
{
inq[p0.v]=true;
q[tail]=p0.v;
tail=(tail+1)%maxn;
// q.push(p0.v);
}
}
}
}
return dist[T];
}
int main()
{
int t,i,j,k;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i=0;i<(n-1)*(n-1)+2;i++)
e[i].clear();
S=(n-1)*(n-1);
T=(n-1)*(n-1)+1;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
scanf("%d",&k);
p0.w=k;
if(i==0&&j!=n-1)
{
p0.v=j;
e[S].push_back(p0);
// p0.v=S;
// e[j].push_back(p0);
}
if(j==n-1&&i!=n-1)
{
p0.v=i*(n-1)+j-1;
e[S].push_back(p0);
// p0.v=S;
// e[i*(n-1)+j-1].push_back(p0);
}
if(j==0&&i!=n-1)
{
p0.v=T;
e[i*(n-1)].push_back(p0);
// p0.v=i*(n-1);
// e[T].push_back(p0);
}
if(i==n-1&&j!=n-1)
{
p0.v=T;
e[(n-2)*(n-1)+j].push_back(p0);
// p0.v=(n-2)*(n-1)+j;
// e[T].push_back(p0);
}
if(i!=n-1&&j!=n-1)
{
if(i)
{
p0.v=(i-1)*(n-1)+j;
e[i*(n-1)+j].push_back(p0);
p0.v=i*(n-1)+j;
e[(i-1)*(n-1)+j].push_back(p0);
}
if(j)
{
p0.v=i*(n-1)+j-1;
e[i*(n-1)+j].push_back(p0);
p0.v=i*(n-1)+j;
e[i*(n-1)+j-1].push_back(p0);
}
}
}
}
printf("%d\n",SPFA());
}
return 0;
}