BJOI2006 狼抓兔子

题目描述

 

这道题可以看出来是最小割的板子题,不过因为这道题的n,m都到了1000,所以总点数是10^6,直接跑最小割会超时。

于是我们要新引入一个概念:对偶图。

我们先说一下什么是平面图。平面图就是所有的边只在顶点处相交。

比如上面的图就是一个平面图。

对于每一个平面图,都有与之对应的对偶图。平面图中边与边之间围成的区域叫面,最外面一个区域也被视作一个面。对偶图就是把每个平面图中的面都视为一个点,对于面与面之间的边,就在面所对应的点之间建立一条双向边,边权与原来相同。如果面的两侧都是自己,那么就连一条自环的边就可以了。

这样我们就发现一条非常重要的性质,如果我们把原点和汇点连一条边,把上半部分视为超级源点,下半部分视为超级汇点,那么我们只要跑一遍对偶图上的最短路,其数值大小即等于原图最小割。直观的理解一下,每条对偶图上的边都可以看作是在原图上的一条杠,而如果想用这些杠把整个图隔断,(最小割)这些杠一定是可以连起来的。

(如果感到难懂可以看一下dalao的图解):传送门

所以只要转化为对偶图以后,跑一遍dij就可以了。

回来看这道题,是非常明显的平面图。所以我们把它的对偶图建出来,直接跑最短路就可以了。

不过其实平面图挺难建的……而且要注意的是,转化为平面图之后点有可能会变多,数组不要开小,预处理的时候不要忘记处理。

看一下代码。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<set>
#include<utility>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
const int INF = 1e9;
const int M = 2000005;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
    }
    return ans * op;
}
struct node
{
    int next,to,v,from;
} e[M<<3];
set <pr> q;
set <pr> :: iterator it;
int n,m,head[M<<1],maxflow = 0,d,dis[M<<1],ecnt,minn = INF;
int source,sink;
void add(int x,int y,int z)
{
    e[++ecnt].to = y;
    e[ecnt].v = z;
    e[ecnt].from = x;
    e[ecnt].next = head[x];
    head[x] = ecnt;
}
void build()
{
    rep(i,1,n)
    {
        rep(j,1,m-1)
        {
            d = read();
            if(i == 1) add(2*j,sink,d),add(sink,2*j,d);
            else if(i == n) add(j*2-1 + 2*(m-1)*(n-2),source,d),add(source,j*2-1 + 2*(m-1)*(n-2),d);
            else
            {
                add(j*2-1 + 2*(m-1)*(i-2),j*2 + 2*(m-1)*(i-1),d);
                add(j*2 + 2*(m-1)*(i-1),j*2-1 + 2*(m-1)*(i-2),d);
            }
        }
    }
    rep(i,1,n-1)
    {
        rep(j,1,m)
        {
            d = read();
            if(j == 1) add(source,j + 2*(m-1)*(i-1),d),add(j+2*(m-1)*(i-1),source,d);
            else if(j == m) add(2*(m-1)*i,sink,d),add(sink,2*(m-1)*i,d);
            else
            {
                add(2*j-2 + 2*(m-1)*(i-1),2*j-1 + 2*(m-1)*(i-1),d);
                add(2*j-1 + 2*(m-1)*(i-1),2*j-2 + 2*(m-1)*(i-1),d);
            }
        }
    }
    int tot = -1;
    rep(i,1,n-1)
    {
        rep(j,1,m-1)
        {
            d = read(),tot += 2;
            add(tot,tot+1,d),add(tot+1,tot,d);
        }
    }
}
void dij(int s)
{
    dis[s] = 0;
    q.insert(make_pair(dis[s],s));
    while(!q.empty())
    {
        pr now = *(q.begin());
        q.erase(q.begin());
        for(int i = head[now.second]; i; i = e[i].next)
        {
            if(dis[e[i].to] > dis[now.second] + e[i].v)
            {
                it = q.find(make_pair(dis[e[i].to],e[i].to));
                if(it != q.end()) q.erase(it);
                dis[e[i].to] = dis[now.second] + e[i].v;
                q.insert(make_pair(dis[e[i].to],e[i].to));
            }
        }
    }
}
int main()
{
    n = read(),m = read();
    if(n == 1)
    {
        rep(i,1,m-1) d = read(),minn = min(minn,d);
        printf("%d\n",minn);
        return 0;
    }
    if(m == 1)
    {
        rep(i,1,n-1) d = read(),minn = min(minn,d);
        printf("%d\n",minn);
        return 0;
    }
    source = 2*(n-1)*(m-1) + 1,sink = source + 1;
    build();
    rep(i,1,M) dis[i] = INF;
    dis[source] = INF,dis[sink] = INF;
    dij(source);
    printf("%d\n",dis[sink]);
    return 0;
}

 

转载于:https://www.cnblogs.com/captain1/p/9495437.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值