洛谷2046 BZOJ2007 NOI2010 海拔 平面图最小割

题目链接
题意:
给你一个网格图,正反边边权不同,从海拔低的地方到海拔高的地方的代价是海拔差乘边权,海拔高到海拔低的地方不需要代价。左上角海拔是0,右下角海拔是1,让你任意安排其他点的海拔,使得每条边都被经过一次的代价之和最小。
话说我题意描述的好像有点糟糕啊,看不懂就还是看原题吧。

题解:
分析一下不难想出如果给一个点赋值为除了0和1之外的数一定是不优的,推一下可以得到一个不等关系。然后我们要是手画几个图我们会发现,我们01交界处应该是连续的一条线,这个yy一下或者造几个例子应该就会发现,然后我们相当于要分割开,就会想到要最小割。
由于题目是网格图,属于平面图,所以可以平面图转对偶图之后求最短路来解决这种最小割问题。
这两天刚学了平面图转对偶图这种东西。我来简单地根据我的理解介绍一下,具体可以看这个讲解。我感觉就是把原图中每一个封闭的图形看作一个点,两个图形相邻就在两个新点之间连边,对于原图四周的边,我们建立源点和汇点与它们连边,为了满足是个割,源点和汇点不再是原图源点和汇点的位置,而是源点在左下(右上),汇点在右上(左下),然后源点看作与左侧和下侧相邻,负责连左侧和下侧的边,汇点负责右侧和上侧,这样建出来的图才会发现走过某条路径相当于割掉某条路径,也是因为这个原因,我们把最小割转化为了最短路问题。

记着要用堆优迪杰,SPFA真的可以被卡,不仅刚过去的NOI2018卡了SPFA,据说这题也是卡SPFA了,奉劝看到这篇文章的各位在大赛中就不要用SPFA这种复杂度不太对的东西了。
最后就是平面图最小割转对偶图最短路的题的时候一定脑子清楚一点,连边的时候想清楚编号,连边时记得是给对偶图连边,别连着连着连成平面图了。
记得开long long

代码:

#include <bits/stdc++.h>
using namespace std;

int n,st,ed,hed[5000010],cnt,vis[1000010];
struct node
{
    int to,next;
    long long dis;
}a[12000010];
long long dis[5000010];
priority_queue <pair<long long,int> > q;
inline void add(int from,int to,long long dis)
{ 
    a[++cnt].to=to;
    a[cnt].dis=dis;
    a[cnt].next=hed[from];
    hed[from]=cnt;
}
int main()
{
    scanf("%d",&n);
    st=10*(n+1)*(n+1)+1;
    ed=st+1;
    for(int i=1;i<=n+1;++i)
    {
        for(int j=1;j<=n;++j)
        {
            long long x;
            scanf("%lld",&x);
            if(i==1)
            {
                add(j+(i-1)*n,ed,x);
            }
            if(i==n+1)
            {
                add(st,j+(i-2)*n,x);
            }
            if(i!=n+1&&i!=1)
            {
                add(j+(i-1)*n,j+(i-2)*n,x);
            }
        }
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n+1;++j)
        {
            long long x;
            scanf("%lld",&x);
            if(j==1)
            {
                add(st,(i-1)*n+j,x);
            }
            if(j==n+1)
            {           
                add((i-1)*n+j-1,ed,x);
            }
            if(j!=1&&j!=n+1)
            {
                add((i-1)*n+j-1,(i-1)*n+j,x);
            }
        }
    }
    for(int i=1;i<=n+1;++i)
    {
        for(int j=1;j<=n;++j)
        {
            long long x;
            scanf("%lld",&x);
            if(i==1)
            {
                add(ed,j+(i-1)*n,x);
            }
            if(i==n+1)
            {
                add(j+(i-2)*n,st,x);
            }
            if(i!=n+1&&i!=1)
            {
                add(j+(i-2)*n,j+(i-1)*n,x);
            }
        }
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n+1;++j)
        {
            long long x;
            scanf("%lld",&x);
            if(j==1)
            {
                add((i-1)*n+j,st,x);
            }
            if(j==n+1)
            {
                add(ed,(i-1)*n+j-1,x);
            }
            if(j!=1&&j!=n+1)
            {
                add((i-1)*n+j,(i-1)*n+j-1,x);
            }
        }
    }
    memset(dis,0x3f,sizeof(dis));
    dis[st]=0;
    q.push(make_pair(0,st));
    while(!q.empty())
    {
        int x=q.top().second;
        q.pop();
        if(vis[x])
        continue;
        vis[x]=1;
        for(int i=hed[x];i;i=a[i].next)
        {
            int y=a[i].to;
            if(dis[y]>dis[x]+a[i].dis)
            {
                dis[y]=dis[x]+a[i].dis;
                q.push(make_pair(-dis[y],y));
            }
        }
    }
    printf("%lld\n",dis[ed]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/81488621