首先应该能看出来这道题求的是最小割(最大流)。然后再看出来这道题的图是个平面图,然后就可以关于这个平面图建出它的对偶图,在这个对偶图上跑最短路就是答案了。直接跑网络流的Dinic应该是T的。
然而,这需要知道什么是平面图,以及如何构建这个对偶图。
边的交点都是顶点的图就是平面图。注意完全图K4也是平面图,详细可见百度百科:http://baike.baidu.com/view/143351.htm 或维基百科:https://zh.wikipedia.org/wiki/%E5%B9%B3%E9%9D%A2%E5%9B%BE_(%E5%9B%BE%E8%AE%BA)
另外,关于平面图f = m - n + 2的证明(f,平面数,n,定点数,m,边数),我想说一下自己的想法:平面图的平面数即这个图把所在平面分成的部分数。那么,树固然是只有一个平面——无界面。我们往一棵树上加边,每加一条边,都会多一个“小环”,就多“一块”,即多一个面。一个连通图,肯定可以从其中摘出一棵树。所以f = m - n + 2就很好理解了。
对于一个平面图G,它的对偶图G*,满足:
G中的一个面对应G*中的一个点;
G中的一条边,连接两个面f1, f2对应G*中的一条边(f1*, f2*)
G中的一条边,仅仅属于一个面f,对应G*中的一条回边(f*,f*)
(注意G中的一条边最多属于两个面)
这样就可以建出对偶图G*,它和G有这样的关系:
f(G) = n(G*), m(G) = m(G*)
G*中的环一一对应对应G中的割
知道了这样的一个性质,那么就可以做到通过求G*中的一个最小环来求G中的最小割了(G中的边的容量对应G*中的边权)。
总而言之,一个图G满足以下条件:
1.是平面图
2.源点s和汇点t都在无界面的边界上
之后就可以这样建立对偶图G*
1.添加一条边(s,t),这样会多出一个面,令这个面为图G*中的起点s*
2.令无界面为图G*中的终点t*
3.通过之前描述的建图方式连边
4.删除1.中添加的边对应的G*中的s*到t*的边
稍微想一下便知道,现在直接跑最短路算法即可得出答案。
QaQ,这道题也是比较水,一眼就能看出是平面图,无需判断,而且很容易建立对偶图G*。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int get ()
{
char c = getchar(); int x = 0;
while (c < '0' || c > '9') c = getchar();
while (c <= '9' && c >= '0') x = x*10+c-48, c = getchar();
return x;
}
int n, m, MIN = 1<<30, hen[1005][1005], zon[1005][1005], xie[1005][1005];
int e, h[2000005], nx[6000005], to[6000005], w[6000005];
int s, t, d[2000005];
bool inq[2000005];
queue <int> q;
void addedge (int a, int b, int v)
{
nx[++e] = h[a], h[a] = e, to[e] = b, w[e] = v;
nx[++e] = h[b], h[b] = e, to[e] = a, w[e] = v;
}
void Initialize ()
{
n = get(), m = get();
for (int i = 1; i <= n; i++)
for (int j = 1; j < m; j++)
MIN = min(MIN, hen[i][j] = get());
for (int i = 1; i < n; i++)
for (int j = 1; j <= m; j++)
MIN = min(MIN, zon[i][j] = get());
for (int i = 1; i < n; i++)
for (int j = 1; j < m; j++)
MIN = min(MIN, xie[i][j] = get());
}
void Get_newG ()
{
if (n == 1 || m == 1) return ;
s = 0, t = (n-1)*(m-1)*2+1;
for (int i = 1; i < m; i++)
{
addedge (s, i, hen[1][i]);
addedge (t, t-m+i, hen[n][i]);
}
for (int i = 2; i < n; i++)
for (int j = 1; j < m; j++)
{
int a = (i-2)*(m-1)*2+m-1+j;
addedge (a, a+m-1, hen[i][j]);
}
for (int i = 1; i < n; i++)
{
addedge (t, (i-1)*(m-1)*2+m, zon[i][1]);
addedge (s, (i-1)*(m-1)*2+m-1, zon[i][m]);
}
for (int i = 1; i < n; i++)
for (int j = 2; j < m; j++)
{
int a = (i-1)*(m-1)*2+j-1;
addedge (a, a+m, zon[i][j]);
}
for (int i = 1; i < n; i++)
for (int j = 1; j < m; j++)
{
int a = (i-1)*(m-1)*2+j;
addedge (a, a+m-1, xie[i][j]);
}
}
void Spfa ()
{
if (n == 1 || m == 1) {printf ("%d\n", MIN); return;}
memset (d, 0x3f, sizeof d);
d[s] = 0; q.push(s);
while (!q.empty())
{
int x = q.front(); q.pop(); inq[x] = 0;
for (int i = h[x]; i; i = nx[i])
if (d[to[i]] > d[x]+w[i]){
d[to[i]] = d[x] + w[i];
if (!inq[to[i]]) q.push(to[i]);
inq[to[i]] = 1;
}
}
printf ("%d\n", d[t]);
}
int main ()
{
Initialize();
Get_newG();
Spfa();
return 0;
}