对偶图
一个图的对偶图如下:
黑点为原图,红点为对偶图
平面图每一个面是对偶图的每一个点
平面图中面与面的割线是对偶图的边
若平面图中某一条边只属于一个面,那么在对偶图中就是一个环边
平面图周围无边界的面也是对偶图中的一个点
网格图网络流
如图,要求
(
1
,
1
)
(1, 1)
(1,1) 到
(
n
,
m
)
(n, m)
(n,m) 的最小割
一条割边相当于一条杠,在网格图网络流中,源点和汇点不连通后,这些杠是连续的
因此,就可以在割线上跑最短路,边权即为原图的权值
然后要设最短路的源点和汇点,这里我们要使得左上至右下不连通,因此源点和汇点分别在左下和右上
例题1:BZOJ 1001 狼抓兔子
题目即为上述的图,注意这里不是严格的网格图,但是同样具有网格图网络流的性质
因此将每个小三角当成一个点建图即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 6e6 + 10;
const ll inf = (ll)1e16;
int n, m, cnt, st, ed;
int vis[maxn], head[maxn];
ll dis[maxn];
struct EDGE{
int next, to, w;
} edge[maxn];
inline void add(int u, int v, int w) {
edge[cnt].next = head[u];
edge[cnt].w = w;
edge[cnt].to = v;
head[u] = cnt++;
}
struct Node{
int id; ll d;
Node() {}
Node(int id, ll d):id(id),d(d){}
bool operator < (const Node &A) const {
return d > A.d;
}
};
void dijkstra(int st){
for(int i=0; i<=ed; i++){
vis[i] = 0;
dis[i] = inf;
}
dis[st] = 0;
priority_queue <Node> Q;
Q.push(Node(st, 0));
Node nd;
while(!Q.empty()){
nd = Q.top(); Q.pop();
if(vis[nd.id]) continue;
vis[nd.id] = true;
for(int i=head[nd.id]; ~i; i=edge[i].next) {
int j = edge[i].to;
int k = edge[i].w;
if(nd.d + k < dis[j] && !vis[j]){
dis[j] = nd.d + k;
Q.push(Node(j, dis[j]));
}
}
}
}
int main(){
scanf("%d%d", &n, &m); int w;
st = 0, ed = (n - 1) * (m - 1) * 2 + 1;
memset(head, -1, sizeof(head));
for(int i=1, tot=2; i<=n; i++)
for(int j=1; j<m; j++, tot+=2) {
scanf("%d", &w);
int u = i == 1 ? ed : tot - m * 2 + 1, v = i == n ? 0 : tot;
add(u, v, w), add(v, u, w);
}
for(int i=1, tot=1; i<n; i++){
for(int j=1; j<m; j++, tot+=2) {
scanf("%d", &w);
int u = j == 1 ? 0 : tot - 1, v = tot;
add(u, v, w), add(v, u, w);
}
scanf("%d", &w);
add(tot-1, ed, w), add(ed, tot-1, w);
}
for(int i=1, tot=1; i<n; i++)
for(int j=1; j<m; j++, tot+=2) {
scanf("%d", &w);
add(tot, tot+1, w), add(tot+1, tot, w);
}
dijkstra(st);
printf("%lld", dis[ed]);
}
例题2:2020 牛客多校第二场 I Interval
题目大意:
初始区间为
(
1
,
n
)
(1,n)
(1,n) ,可以自由萎缩或扩张,定义
(
l
,
r
,
d
i
r
,
c
)
(l,r,dir,c)
(l,r,dir,c)
当
d
i
r
=
L
dir = L
dir=L 时,可以花费
c
c
c 的代价
b
a
n
ban
ban 掉
(
l
,
r
)
(l, r)
(l,r) 与
(
l
+
1
,
r
)
(l+1, r)
(l+1,r) 之间的双向更改
当
d
i
r
=
R
dir = R
dir=R 时,可以花费
c
c
c 的代价
b
a
n
ban
ban 掉
(
l
,
r
)
(l, r)
(l,r) 与
(
l
,
r
−
1
)
(l, r-1)
(l,r−1) 之间的双向更改
问最小代价,使其不能变为
(
l
,
r
)
(l, r)
(l,r),且
l
=
=
r
l == r
l==r
解题思路:
很明显是要求最小割,设源点为
(
1
,
n
)
(1, n)
(1,n),汇点为
(
n
,
1
)
(n, 1)
(n,1)
将所有
(
i
,
i
)
(i, i)
(i,i) 与汇点连一条无穷流量的边即可,无限制的两点也连无穷流量的边
因此就可以转对偶图来优化,注意这里的源点和汇点是右上和左下
因此最短路的起点和终点要设为右下和左上,就能切开原图
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 3e6 + 10;
const ll inf = (ll)1e16;
int n, m, cnt, st, ed;
int vis[maxn], head[maxn];
int id[1005][1005];
ll w[1005][1005][2];
ll dis[maxn];
struct EDGE{
int next, to;
ll w;
} edge[maxn];
inline void add(int u, int v, ll w) {
edge[cnt].next = head[u];
edge[cnt].w = w;
edge[cnt].to = v;
head[u] = cnt++;
}
struct Node{
int id; ll d;
Node() {}
Node(int id, ll d):id(id),d(d){}
bool operator < (const Node &A) const {
return d > A.d;
}
};
void dijkstra(int st){
for(int i=0; i<=ed; i++){
vis[i] = 0;
dis[i] = inf;
}
dis[st] = 0;
priority_queue <Node> Q;
Q.push(Node(st, 0));
Node nd;
while(!Q.empty()){
nd = Q.top(); Q.pop();
if(vis[nd.id]) continue;
vis[nd.id] = true;
for(int i=head[nd.id]; ~i; i=edge[i].next) {
int j = edge[i].to;
ll k = edge[i].w;
if(nd.d + k < dis[j] && !vis[j]){
dis[j] = nd.d + k;
Q.push(Node(j, dis[j]));
}
}
}
}
int main(){
scanf("%d%d", &n, &m);
st = 0, ed = n * n + 1;
memset(head, -1, sizeof(head));
for(int i=1, cnt=0; i<n; i++)
for(int j=i+1; j<=n; j++) {
w[i][j][0] = w[i][j][1] = inf;
id[i][j] = ++cnt;
}
for(int i=1; i<=m; i++) {
int l, r, c; char dir;
scanf("%d%d %c %d", &l, &r, &dir, &c);
if(dir == 'L') w[l][r][0] = min(w[l][r][0], (ll)c);
else w[l][r][1] = min(w[l][r][1], (ll)c);
}
for(int i=1; i<n; i++)
for(int j=i+1; j<=n; j++) {
if(j < n) {
add(id[i][j], id[i][j+1], w[i][j][0]);
add(id[i][j+1], id[i][j], w[i][j][0]);
}
if(i > 1) {
add(id[i-1][j], id[i][j], w[i][j][1]);
add(id[i][j], id[i-1][j], w[i][j][1]);
}
}
for(int i=1; i<n; i++) add(st, id[i][n], w[i][n][0]);
for(int i=2; i<=n; i++) add(id[1][i], ed, w[1][i][1]);
dijkstra(st);
printf("%lld", dis[ed]>=inf ? -1 : dis[ed]);
}