BZOJ1001(BeiJing2006)[狼抓兔子]题解--最小割&spfa

【链接】
bzoj1001

【题目大意】
给你一张网格图,让你将图中的删去一些边,从而使没有一条路径从左上角到右下角,答案即为所有删去边的加和的最小值。

【解题报告】
本题一看就发现是最小割,但是时间有些高,所以就换一个思路想,发现似乎可以用最短路做,因为是求一条权值最小的路径,果断刷spfa,将边转化成点,建边只需要将每个小三角形之间的边互相建边即可。

【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=2996005,maxm=11976017,INF=((1<<30)-1)*2+1;
int n,m,ans,tot,lnk[maxn],que[maxn],dst[maxn],w[maxn],son[maxm],nxt[maxm];
bool vis[maxn];
inline int Read()
{
    int res=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') res=res*10+ch-48,ch=getchar();
    return res;
}
int Getid(int id,int x,int y)
{
    if (id==0) return (x-1)*(m-1)+y;
     else if (id==1) return n*(m-1)+(x-1)*m+y;
      else return n*(m-1)+(n-1)*m+(x-1)*(m-1)+y;
}
int Add(int x,int y)
{
    son[++tot]=y; nxt[tot]=lnk[x]; lnk[x]=tot;
}
int Spfa()
{
    memset(vis,0,sizeof(vis));
    memset(dst,63,sizeof(dst));
    int hed=0,til=0,id;
    for (int i=1; i<m; i++) id=Getid(0,n,i),que[++til]=id,vis[id]=1,dst[id]=0;
    for (int i=1; i<n; i++) id=Getid(1,i,1),que[++til]=id,vis[id]=1,dst[id]=0;
    while (hed!=til)
    {
        hed=(hed+1)%maxn;
        int x=que[hed]; vis[x]=0;
        for (int j=lnk[x]; j; j=nxt[j])
         if (dst[x]+w[x]<dst[son[j]])
          {
            dst[son[j]]=dst[x]+w[x];
            if (!vis[son[j]])
             {
                vis[son[j]]=1; til=(til+1)%maxn; que[til]=son[j];
                if (dst[que[til]]<dst[que[(hed+1)%maxn]]) swap(que[til],que[(hed+1)%maxn]); 
             }
          }
    }
    for (int i=1; i<m; i++) id=Getid(0,1,i),ans=min(ans,dst[id]+w[id]);
    for (int i=1; i<n; i++) id=Getid(1,i,m),ans=min(ans,dst[id]+w[id]);
    return 0;
}
int main()
{
    freopen("1001.in","r",stdin);
    freopen("1001.out","w",stdout);
    n=Read(); m=Read(); tot=0; ans=1<<30;
    if (n==1&&m==1) {printf("0"); return 0;}
    for (int i=1; i<=n; i++)
     for (int j=1; j<m; j++)
      w[Getid(0,i,j)]=Read();
    for (int i=1; i<n; i++)
     for (int j=1; j<=m; j++)
      w[Getid(1,i,j)]=Read();
    for (int i=1; i<n; i++)
     for (int j=1; j<m; j++)
      w[Getid(2,i,j)]=Read();
    for (int i=1; i<n; i++)
     for (int j=1; j<m; j++)
      {
        int X1=Getid(0,i+1,j),X2=Getid(1,i,j),X3=Getid(2,i,j);
        Add(X1,X2); Add(X1,X3);
        Add(X2,X1); Add(X2,X3);
        Add(X3,X1); Add(X3,X2);
        //以上是将左下小三角形之间的边建边
        X1=Getid(0,i,j),X2=Getid(1,i,j+1),X3=Getid(2,i,j);
        Add(X1,X2); Add(X1,X3);
        Add(X2,X1); Add(X2,X3);
        Add(X3,X1); Add(X3,X2);
        //以上是将右上小三角形之间的边建边
      }
    Spfa(); 
    printf("%d",ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值