【NOI2010】海拔

题面

https://www.luogu.org/problem/P2046

题解

首先,显然,一个地方的高度不是$0$,就是$1$。

我们设计一个网络流模型,保留原图中的边,以$(1,1)$为源,$(n,n)$为汇,求最小割(纯口胡,正确性不敢保证)

接着因为数据太大,平面图转对偶图。

平面图最小割即对偶图最短路,

我们把左、上边沿外点记为$S$,下、右边沿外的点记为$T$,

原来的向下的人流变成向右的路径(为什么?因为向下的人流代表了上面是$0$,而下面是$1$,$S$在左而$T$在右,所以从左到右)

向上的人流变成向左的路径(下$0$上$1$,只有路径向左走时才会出现这样的情况)

左右方向的人流同理。

虽然这样正边和反边的权值不一样,有点难理解,但只要按照那套理论和画的图像一步步推就可以了。

刚开始拿到这道题的时候,想到这个细节,觉得不可做,但是再仔细想想就会做了。要是在考试的时候这样可不行啊。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define ri register int
#define LL long long 
#define N 300050
#define S 0
#define T (n*n+1)
using namespace std;

inline int read() {
  int ret=0,f=0; char ch=getchar();
  while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar();
  while (ch>='0' && ch<='9') ret*=10,ret+=(ch-'0'),ch=getchar();
  return f?-ret:ret;
}

int n,id[550][550];
LL dis[N];
bool vis[N];
vector<int> to[N],len[N];

struct HeapNode {
  int u; LL d;
  bool operator < (const HeapNode &rhs) const {
    return d>rhs.d;
  }
};

void add_edge(int u,int v,int w) {
  to[u].push_back(v); len[u].push_back(w);
}

LL dijkstra() {
  priority_queue<HeapNode> pq;
  memset(dis,0x3f,sizeof(dis)); dis[S]=0; pq.push((HeapNode){S,dis[S]});
  while (!pq.empty()) {
    int x=pq.top().u; pq.pop();
    if (vis[x]) continue;
    vis[x]=1;
    for (ri i=0;i<to[x].size();++i) {
      int y=to[x][i];
      if (dis[x]+len[x][i]<dis[y]) {
        dis[y]=dis[x]+len[x][i];
        pq.push((HeapNode){y,dis[y]});
      }
    }
  }
  return dis[T];
}

int main() {
  n=read();
  int cc=0;
  for (ri i=1;i<=n;++i)
    for (ri j=1;j<=n;++j) id[i][j]=++cc;
  for (ri i=1;i<=n;++i) id[n+1][i]=id[i][0]=S,id[i][n+1]=id[0][i]=T;
  for (ri i=1;i<=n+1;++i) 
    for (ri j=1;j<=n;++j) add_edge(id[i][j],id[i-1][j],read());
  for (ri i=1;i<=n;++i)
    for (ri j=1;j<=n+1;++j) add_edge(id[i][j-1],id[i][j],read());
  for (ri i=1;i<=n+1;++i)
    for (ri j=1;j<=n;++j) add_edge(id[i-1][j],id[i][j],read());
  for (ri i=1;i<=n;++i)
    for (ri j=1;j<=n+1;++j) add_edge(id[i][j],id[i][j-1],read());
  printf("%lld\n",dijkstra());
  return 0;
}

 

转载于:https://www.cnblogs.com/shxnb666/p/11347694.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值