D85 对偶图最短路 Dijkstra 算法 P2046 [NOI2010] 海拔

D85 对偶图最短路 Dijkstra 算法 P2046 [NOI2010] 海拔_哔哩哔哩_bilibili

 

P2046 [NOI2010] 海拔 - 洛谷

思路

贪心思考,所有格点的高度只能取 0 或 1,否则变差,并且对偶图的最短路径一定把平面图的格点分成两部分,

右下连通格点的高度均为 1,左上连通格点的高度均为 0,最短路径上的格边如果从 0 指向 1,那么边权有效,

这样,平面图的 最小割 转化为 对偶图的 最短路

类似 D83 模板,每个小方格内建一点,写个开点函数 get(x,y) = (x-1)*(n-1)+y

发现右手性质,从 s 到 t 沿着最短路径走,右手边一定是高度为 1 的格点,这样看图连对偶图的边就好

 image

建好对偶图后,跑一遍 Dijktra,答案就是 d[t]

点数 N = 500*500+2 < 3e5,边数 M = 501*500*2*2 < 1.1e6

相关板子:

D84【模板】对偶图最短路 Dijkstra 算法 P4001 [ICPC-Beijing 2006] 狼抓兔子 - 董晓 - 博客园

 

// 对偶图最短路 Dijkstra 算法 O(MlogN)
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;

const int N=3e5,M=1.1e6;
int to[M],ne[M],ww[M],h[N],idx;
void add(int a,int b,int c){
  to[++idx]=b,ww[idx]=c,ne[idx]=h[a],h[a]=idx;
}
int n,s,t;
int d[N];
bool vis[N];

void dijkstra(){
  memset(d,0x3f,sizeof d); d[s]=0;
  priority_queue<pii,vector<pii>,greater<pii> > q;
  q.push({0,s});
  while(!q.empty()){
    int u=q.top().second;q.pop();
    if(vis[u])continue;vis[u]=1;
    for(int i=h[u];i;i=ne[i]){
      int v=to[i],w=ww[i];
      if(d[v]>d[u]+w){
        d[v]=d[u]+w;
        q.push({d[v],v});
      }
    }
  }
}
int get(int x,int y){
  return (x-1)*(n-1)+y;
}
int main(){
  cin>>n; n++;  //变成边数
  s=0; t=n*n+1; //t对偶图终点编号
  int w,v1,v2;
  for(int i=1;i<=n;++i)for(int j=1;j<n;++j){ //从左到右
    scanf("%d",&w);
    v1=get(i-1,j); //上点
    v2=get(i,j);   //下点
    if(i==1) add(v2,t,w);
    else if(i==n) add(s,v1,w);
    else add(v2,v1,w); //右格点高,下点连上点
  }
  for(int i=1;i<n;++i)for(int j=1;j<=n;++j){ //从上到下
    scanf("%d",&w);
    v1=get(i,j-1); //左点
    v2=get(i,j);   //右点
    if(j==1) add(s,v2,w);
    else if(j==n) add(v1,t,w);
    else add(v1,v2,w); //下格点高,左点连右点
  }
  for(int i=1;i<=n;++i)for(int j=1;j<n;++j){ //从右到左
    scanf("%d",&w);
    v1=get(i-1,j); //上点
    v2=get(i,j);   //下点
    if(i==1) add(t,v2,w);
    else if(i==n) add(v1,s,w);
    else add(v1,v2,w); //左格点高,上点连下点
  }
  for(int i=1;i<n;++i)for(int j=1;j<=n;++j){ //从下到上
    scanf("%d",&w);
    v1=get(i,j-1); //左点
    v2=get(i,j);   //右点
    if(j==1) add(v2,s,w);
    else if(j==n) add(t,v1,w);
    else add(v2,v1,w); //上格点高,右点连左点
  }
  
  dijkstra();
  printf("%d",d[t]);
}
 

 

posted @ 2026-02-24 09:32  董晓  阅读(22)  评论(0)    收藏  举报