#edu3031. 对偶图最短路
对偶图最短路
经典例子:
例1:P4001 [ICPC-Beijing 2006]狼抓兔子
本题表面上是最小割,利用最大流可以求最小割,但是裸的dinic对n*m(n,m<=1000)会炸掉,虽然优化后可以通过,但是复杂度依然玄学,本题是网格图上求最大割,可以转化为对偶图求其最短路。
对偶图就是把图中分成的块看作新图中的一个点,原图的边连着连个块,如下图:
狼抓兔子-1
红色数字的就是新图中的点,原图中的边如果连接了两个新图中两个点,就是新图中的边,这种转化就把原图转成了对偶图。
对偶图有很多性质,其中一个重要的性质就是原图的最小割-最大流,就是对偶图起点到终点的最短路,原因如下图:
狼抓兔子-2
从起点到终点的路径就是原图的一个割,那么最短路径肯定就是最小割。
参考代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
struct node{
int to,dis,next;
}edge[N*3];
int head[N],tot;
int n,m;
int d[N],vis[N];
void add(int from,int to,int dis)
{
edge[++tot].to=to; edge[tot].dis=dis;
edge[tot].next=head[from];
head[from]=tot;
}
void dijkstra(int s)
{
priority_queue<pair<int ,int> >que;
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
que.push(make_pair(0,s));
d[s]=0;
while(que.size())
{
int x=que.top().second;que.pop();
if(vis[x])continue;
vis[x]=1;
for(int i=head[x];i;i=edge[i].next)
{
int y=edge[i].to,w=edge[i].dis;
if(d[y]>d[x]+w)
{
d[y]=d[x]+w;
que.push(make_pair(-d[y],y));
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
int st,ed;
st=(n-1)*(m-1)*2+1;
ed=st+1;
int blok1,blok2,val;
for(int i=1;i<=n;i++)
for(int j=1;j<m;j++)
{
if(i<n)
blok1=(i-1)*(m-1)*2+j;
else
blok1=st;
if(i>1)
blok2=(i-2)*(m-1)*2+m-1+j;
else
blok2=ed;
scanf("%d",&val);
add(blok1,blok2,val);
add(blok2,blok1,val);
}
for(int i=1;i<n;i++)
for(int j=1;j<=m;j++)
{
if(j<m)
blok1=(i-1)*(m-1)*2+m-1+j;
else
blok1=ed;
if(j>1)
blok2=(i-1)*(m-1)*2+j-1;
else
blok2=st;
scanf("%d",&val);
add(blok1,blok2,val);
add(blok2,blok1,val);
}
for(int i=1;i<n;i++)
for(int j=1;j<m;j++)
{
blok1=(i-1)*(m-1)*2+j;
blok2=blok1+m-1;
scanf("%d",&val);
add(blok1,blok2,val);
add(blok2,blok1,val);
}
dijkstra(st);
printf("%d",d[ed]);
return 0;
}
学习完毕
{{ select(1) }}
- YES
- NO