BZOJ2007: [Noi2010]海拔

13 篇文章 0 订阅
12 篇文章 0 订阅

首先最优解一定可以是左上的一片区域海拔为0,右下的一片区域海拔为1
证明的话,如果有很多小数的话,对于一个海拔为小数的格子,他的海拔变成相邻的格子中的一个的海拔,一定不比原来劣,那么对于海拔相同的,连在一起的格子视为一个块,这个块的海拔若是小数,他仍然可以按上文的方式合并到其他块,最后只剩一块0和一块1(为什么0和1不能合并?能合并当然好但是题目规定了两个点的海拔)

然后花费就来自于0和1的分界线,那么就变成了最小割了

然后因为很慢跑不过去
观察到这是一个平面图,将它转成对偶图,原图的最小割对应对偶图的最短路,就可以了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9+10
using namespace std;

inline void read(int &x)
{
    char c; 
    while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 260000;
const int maxm = maxn*4;

int n,m,st,ed;

struct edge
{
    int y,c,nex;
}a[maxm]; int len,fir[maxn];
void ins(const int x,const int y,const int c)
{
    a[++len].y=y; a[len].c=c;
    a[len].nex=fir[x]; fir[x]=len;
}

struct node
{
    int x,i;
    node(){}
    node(const int &_x,const int &_i){x=_x;i=_i;}
};
inline bool operator <(node x,node y){return x.x>y.x;}
priority_queue<node>q;

int d[maxn];
void search()
{
    for(int i=0;i<=ed;i++) d[i]=inf;
    d[st]=0; q.push(node(0,st));
    while(!q.empty())
    {
        node x=q.top(); q.pop();
        if(x.x!=d[x.i]) continue;
        for(int k=fir[x.i];k;k=a[k].nex)
        {
            const int y=a[k].y;
            if(d[y]>x.x+a[k].c)
            {
                d[y]=x.x+a[k].c;
                q.push(node(d[y],y));
            }
        }
    }
}

int main()
{
    len=0; memset(fir,0,sizeof fir);

    read(n); st=n*n+1,ed=st+1;
    for(int i=1,id=1;i<=n+1;i++)
        for(int j=1;j<=n;j++,id++)
        {
            int x; read(x);
            if(i==1) ins(id,ed,x);
            else if(i==n+1) ins(st,id-n,x);
            else ins(id,id-n,x);
        }
    for(int i=1,id=1;i<=n;i++)
        for(int j=1;j<=n+1;j++,id++)
        {
            int x; read(x);
            if(j==1) ins(st,id,x);
            else if(j==n+1) ins(--id,ed,x);
            else ins(id-1,id,x);
        }
    for(int i=1,id=1;i<=n+1;i++)
        for(int j=1;j<=n;j++,id++)
        {
            int x; read(x);
            if(i==1) continue;
            else if(i==n+1) continue;
            else ins(id-n,id,x);
        }
    for(int i=1,id=1;i<=n;i++)
        for(int j=1;j<=n+1;j++,id++)
        {
            int x; read(x);
            if(j==1) continue;
            else if(j==n+1) id--;
            else ins(id,id-1,x);
        }

    search();
    printf("%d\n",d[ed]);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值