一开始尝试用最大流做,解不出来,然后只能百度了
题意 :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 <string.h>
#include <stdio.h>
#include <queue>
using namespace std;
const int maxn=444;
const int maxm=maxn*maxn;
const int oo=1000000000;
struct node{
int w,to,next;
}e[maxm*6];
int s;
int head[maxm],dis[maxm];
int a[maxn][maxn];
int edge,n,m;
bool vis[maxm];
queue<int>q;
void add(int u,int v,int c)
{
e[edge].to=v,e[edge].w=c,e[edge].next=head[u],head[u]=edge++;
e[edge].to=u,e[edge].w=c,e[edge].next=head[v],head[v]=edge++;
}
void spfa()
{
int v,i;
while(!q.empty()) q.pop();
q.push(s);
memset(dis, -1, sizeof(dis));
memset(vis,0,sizeof(vis));
//vis[s]=1;
dis[s]=0;
while(!q.empty())
{
int tmp=q.front();
q.pop();
for(i=head[tmp];i!=-1;i=e[i].next)
{
if(dis[tmp]+e[i].w<dis[v=e[i].to]||dis[v=e[i].to]==-1)
{
dis[v]=dis[tmp]+e[i].w;
if(!vis[v])
{
vis[v]=true;
q.push(v);
}
}
}
vis[tmp]=false;
}
}
int main()
{
int t;
int i,j;
cin>>t;
while(t--)
{
s=0;
cin>>n;
int t=(n-1)*(n-1)+1;
edge=0;
memset(head,-1,sizeof(head));
for(i =1; i <= n; i++)
for(j =1; j <= n; j++)
scanf("%d", &a[i][j]);
for(i =1; i < n; i++)
for(j =1; j < n; j++)
{
if(i==1|| j==n-1)
{
if(i ==1) add((i-1)*(n-1)+j, t, a[i][j]);
if(j == n-1) add((i-1)*(n-1)+j, t, a[i][j+1]);
if(i==1&& j<n-1) add((i-1)*(n-1)+j, (i-1)*(n-1)+j+1, a[i][j+1]);
if(i>1&& j==n-1) add((i-1)*(n-1)+j, (i-2)*(n-1)+j, a[i][j]);
}
else
{
add((i-1)*(n-1)+j, (i-2)*(n-1)+j, a[i][j]);
add((i-1)*(n-1)+j, (i-1)*(n-1)+j+1, a[i][j+1]);
}
if(j==1|| i==n-1)
{
if(j ==1) add(s, (i-1)*(n-1)+j, a[i][j]);
if(i == n-1) add(s, (i-1)*(n-1)+j, a[i+1][j]);
if(j==1&& i>1) add((i-1)*(n-1)+j, (i-2)*(n-1)+j, a[i][j]);
if(i==n-1&& j<n-1) add((i-1)*(n-1)+j, (i-1)*(n-1)+j+1, a[i][j+1]);
}
}
spfa();
cout<<dis[t]<<endl;
}
return 0;
}