BZOJ 1001 [BeiJing2006]狼抓兔子

平面图最小割转对偶图最短路。

第一眼看到这题,显然是最小割嘛。。。根据最大流最小割定理,跑一遍最大流即可,但复杂度 O(n2m) ,显然要T啊。

然后我就学习了平面图最小割转对偶图最短路的想法,看完就会做了,资料传送门:两极相通——浅析最大最小定理在信息学竞赛中的应用

注意,对偶图最短路做法只使用于s-t平面图,不一定适用于一般图

这里顺便摘抄一些平面图的重要定义和性质,详细内容看资料:

·平面图:若图G可画在平面上,使得任意两条边都不会在非端点处相交,则称图G是平面图。

·s-t平面图:平面图中的一个点为源点s,另外一个点为汇点t,且s和t都在图中的无界面的边界上,这样的平面图称为s-t平面图

·欧拉公式:如果一个连通的平面图有n个点,m条边,f个域(面),那么f=m-n+2

·性质:每个平面图G都有一个与其对偶的平面图G*

话说这题用SPFA慢出翔了,推荐堆优化dijkstra(复杂度 O((n+m)log2n)

#include<cstdio>
#include<queue>
#include<cstring>
#define add2(u,v,w) add(u,v,w);add(v,u,w)
#define N 1005
using namespace std;
int in()
{
    int r=0;
    char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')r=r*10+ch-'0',ch=getchar();
    return r;
}
queue<int> q;
bool vis[2200000];
int last[2200000], d[2200000], s=0, t=1, cnt=0;
int pack(int x, int y, int side){return (x*1001+y)+side*1002005;}
struct edge{int next,to,v;}e[2*3*N*N];
void add(int a, int b, int w)
{
    e[++cnt]=(edge){last[a],b,w};
    last[a]=cnt;
}
struct node
{
    int id, v;
    node(){}
    node(int a, int b):id(a),v(b){}
    bool operator < (const node &a) const
    {
        return a.v<v;
    }
};
int dijkstra()
{
    priority_queue<node> q;
    memset(d,63,sizeof(d));
    q.push(node(s,d[s]=0));
    vis[s]=1;
    while(!q.empty())
    {
        int x=q.top().id;
        q.pop();
        vis[x]=1;
        if(x==t)break;
        for(int i = last[x]; i; i=e[i].next)
        {
            int y=e[i].to;
            if(!vis[y]&&d[x]+e[i].v<d[y])
            {
                d[y]=d[x]+e[i].v;
                q.push(node(y,d[y]));
            }
        }
    }
    return d[t];
}
int main()
{
    int n, m, len;
    n=in();m=in();
    if(n==1&&m==1)return !printf("0\n");
    for(int i = 1; i <= n; i++)
        for(int j = 1; j < m; j++)
        {
            len=in();
            add2(pack(i,j,1),pack(i-1,j,0),len);
        }
    for(int i = 1; i < n; i++)
        for(int j = 1; j <= m; j++)
        {
            len=in();
            add2(pack(i,j,0),pack(i,j-1,1),len);
        }
    for(int i = 1; i < n; i++)
        for(int j = 1; j < m; j++)
        {
            len=in();
            add2(pack(i,j,0),pack(i,j,1),len);
        }
    for(int j = 1; j <= m; j++)
    {
        add(pack(0,j,0),t,0);
        add(s,pack(n,j,1),0);
    }
    for(int i = 1; i <= n; i++)
    {
        add(pack(i,m,0),t,0);
        add(s,pack(i,0,1),0);
    }
    int ans=dijkstra();
    printf("%d\n",ans);
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值