【BZOJ1001】狼抓兔子

这道题贼坑人,交了5遍才过,最后实在过不去了,拿来了黄学长的代码对拍,发现自己点数的数组开小了+没有判断成一条链时的情况,其实具体我也不太知道为什么我的点过不去,但是用黄学长那个加点法是能过得去m=1的数据的,做法参见《两极相通——浅析最大—最小定理在信息学竞赛中的应用》——周东。
首先这个图是一个裸的最小割,但是呢,因为边数太大了,会超时,又因为它是一个平面图,根据欧拉公式,可以把他的面,转换成它的点,把他的边做一条连接两个面的边,根据这些做出一个反图,然后这个反图的边就是一条割边,所以反图的一条路就是一个割,所以反图的最短路就是平面图的最小割,根据以上,制图,跑一边最短路就出来了,所以这个图的坑点其实还是在作图上,不知道为什么我是把每一行顺次做出(m=4)1.2.3.4.5.6之类的,但是过不去m=1的情况,hzwer是把每一个做成(m=4)1.4.2.5.3.6,他这样的作图方式就能过得去,然而本质并无区别好吗,只不过把点改了一下名字而已。
还有一个坑点就是,他的面最大是(1000-1) * (1000-1) * 2,我一开始把数组开成了1000000,然后终点设为1000001,然后就WA了好几次,也是要注意细节吧,毕竟今天刚刚开始刷BZOJ,感觉这种OJ,真的蛮磨炼个人水平的,题难到不说,主要是他不会告诉你测试数据第几个点错了,也不会告诉你过了几个点,或者你错的点的那个数据,对于细节的把控也是要慢慢提高的。
附代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
const int N=2000005;
int n,m,d[N],inq[N],head[N],te;
struct edge{
    int u,v,next,w;
}e[8000010];
inline int getc()
{
    register int a;register char c;
    while(c=getchar(),c<'-');a=c-'0';
    while(c=getchar(),'-'<c)a=(a<<3)+(a<<1)+c-'0';
    return a;
}
int add(int u,int v,int w)
{
    e[++te].v=v;
    e[te].w=w;
    e[te].next=head[u];
    head[u]=te;
}
void spfa()
{
    queue<int>q;
    memset(d,0x3f,sizeof(d));
    memset(inq,0,sizeof(inq));
    d[0]=0;
    inq[0]=1;
    q.push(0);
    while(!q.empty())
    {
        int u=q.front();
        inq[u]=0;
        q.pop();
        for (int i=head[u];i;i=e[i].next)
        {
            int v=e[i].v;
            if (d[v]>d[u]+e[i].w)
            {
                d[v]=d[u]+e[i].w;
                if (!inq[v])
                {
                    inq[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
int main()
{

//  freopen("std.in","r",stdin);
//  freopen("std.out","w",stdout);
    int ed=2000003;
    te=0;
    memset(head,0,sizeof(head));
    n=getc();m=getc();
    if (m==1)
    {
        int k=0x3f3f3f3f;
        for (int i=1;i<n;i++)
        {
            int w=getc();
            k=min(w,k);
        }   
        cout<<k;
        return 0;
    }
    int w,u,v;
    for (int i=1;i<=n;i++)
    {
        u=(i-1)*(m-1)*2;
        for (int j=1;j<m;j++)
        {
            u+=2;v=u-2*m+1;
            w=getc();   
            if (i==1)add(0,u,w),add(u,0,w);
            else if (i==n)add(v,ed,w),add(ed,v,w);
            else
            {
                add(u,v,w);
                add(v,u,w);
            }
        }
    }
    for (int i=1;i<n;i++)
    {
        u=(i-1)*(m-1)*2+1;
        for (int j=1;j<=m;j++)
        {
            w=getc();
            if (j==1)add(ed,u,w),add(u,ed,w);
            else if (j==m)add(0,u-1,w),add(u-1,0,w);
            else add(u,u-1,w),add(u-1,u,w);
            u+=2;
        }
    }
    for (int i=1;i<n;i++)
    {
        u=(i-1)*(m-1)*2+1;
        for (int j=1;j<m;j++)
        {
            w=getc();
            add(u,u+1,w);
            add(u+1,u,w);
            u+=2;
        }
    }
    spfa();
    cout<<d[ed]<<endl;
//  fclose(stdin);
//  fclose(stdout);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值