bzoj1001 [ICPC-Beijing 2006]狼抓兔子

我满心以为本题正解为最短路,结果到处都是最大流……

几乎所有的都写了什么“对偶图”跑最短路,但我真的不知道什么叫做对偶图
-------------------------------------------------------------------------------------------------
介绍一下本题的最短路算法叭。并不算难。主要是感性理解。

 

首先很容易观察出这是一个最小割,那么就是求最大流了。

但是这题的点数高达10e6,按常理来说最大流应该稳稳地TLE。但是没有T好气哦

那么想办法!

首先最小割在本题时可以这样感性理解:上图是一个你同学在钢铁厂打出来的一个铁架子。你把start处用手捏起来,end处自然垂下。用一个剪刀钳把这个铁架子拦腰剪成两半。

如果剪成好几瓣(掉下来有好几个联通块的),那么显而易见,不如剪成两半(把刚才几个剪断的地方原样拼起来变成两个联通块)。

我们把三角形看成是点,黑色的边看成是连接三角形的边,那么剪成两半的意思是……在三角形点的图上找一条从左下到右上的最短路径!沿着这条路径剪开就行了。

但是这题的点数高达10e6,按常理来说SPFA应该稳稳地TLE。但是没有T好气哦

那就堆优化dijkstra。

这个加边超烦的。但思路清晰的话就没什么问题。记得在左下空白处设一个源点,右上角设一个汇点。源点连接所有邻接它的左边的、下边的三角形点,汇点连接所有邻接它的右边的、上边的三角形点。

#include <cstdio>
#include <queue>
using namespace std;
const int N=1002,S=N*N*6+30,inf=(1<<30)-1;
int n,m,a[N][N],b[N][N],c[N][N],d[S],id[N][N],ss,tt,h[S],v[S],nx[S],w[S],eg=1;
bool vis[S]={0};
struct info
{
    int x,w;
}data;
inline bool operator<(const info &a,const info &b)
{
    return a.w>b.w;
}
priority_queue<struct info> pq;
inline void egadd(int uu,int vv,int ww)
{
    nx[++eg]=h[uu];h[uu]=eg;
    v[eg]=vv;w[eg]=ww;
}
void rd(int &s)
{
    s=0;char c=getchar();
    while (c<48) c=getchar();
    while (c>=48) s=(s<<1)+(s<<3)+(c^48),c=getchar();
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m-1;j++)
            rd(a[i][j]);
    for (int i=1;i<=n-1;i++)
        for (int j=1;j<=m;j++)
            rd(b[i][j]);
    for (int i=1;i<=n-1;i++)
        for (int j=1;j<=m-1;j++)
            rd(c[i][j]);
    n--;m--;
    if (!n)
    {
        int res=inf;
        for (int i=1;i<=m;i++)
            if (a[1][i]<res)
                res=a[1][i];
        printf("%d",res);
        return 0;
    }
    if (!m)
    {
        int res=inf;
        for (int i=1;i<=n;i++)
            if (b[i][1]<res)
                res=b[i][1];
        printf("%d",res);
        return 0;
    }
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            id[i][j]=(i-1)*2*m+j;
    ss=n*2*m+1;tt=ss+1;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            egadd(id[i][j],id[i][j]+m,c[i][j]);
            egadd(id[i][j]+m,id[i][j],c[i][j]);
        }
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m-1;j++)
        {
            egadd(id[i][j],id[i][j+1]+m,b[i][j+1]);
            egadd(id[i][j+1]+m,id[i][j],b[i][j+1]);
        }
    for (int i=1;i<=n-1;i++)
        for (int j=1;j<=m;j++)
        {
            egadd(id[i][j]+m,id[i+1][j],a[i+1][j]);
            egadd(id[i+1][j],id[i][j]+m,a[i+1][j]);
        }
    for (int i=1;i<=m;i++)
    {
        egadd(id[1][i],tt,a[1][i]);
        egadd(ss,id[n][i]+m,a[n+1][i]);
    }
    for (int i=1;i<=n;i++)
    {
        egadd(ss,id[i][1]+m,b[i][1]);
        egadd(id[i][m],tt,b[i][m+1]);
    }
    for (int i=1;i<=tt;i++)
        d[i]=inf;
    d[ss]=0;
    pq.push((info){ss,0});
    while (!pq.empty())
    {
        while (!pq.empty() && vis[pq.top().x])
            pq.pop();
        if (pq.empty()) break;
        data=pq.top();
        pq.pop();
        int x=data.x,ww=data.w;
        printf("%d %d\n",x,ww);
        vis[x]=true;
        for (int i=h[x];i;i=nx[i])
            if (!vis[v[i]] && d[v[i]]>ww+w[i])
            {
                d[v[i]]=ww+w[i];
                pq.push((info){v[i],d[v[i]]});
                printf("Add:%d %d\n",v[i],d[v[i]]);
            }
    }
    printf("%d",d[tt]);
    return 0;
}

 

posted @ 2019-02-14 18:55  Hygebra  阅读(132)  评论(0编辑  收藏  举报