duliu题之狼抓兔子题解

Posted lcez56jsy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了duliu题之狼抓兔子题解相关的知识,希望对你有一定的参考价值。

拖了将近5天的正解和AC.........emmmmm...........

事实告诉我们这种毒瘤题一定要建双向边(用了不知道多少个小时质疑建边的人欲哭无泪)

心态爆炸的传送

题了个面

技术图片

技术图片

这是个求最小割问题

说人话:

 把图中的一些边砍断,使这个图分为不连通的两部分。砍断一条边的代价就是这条边的边权,求最小代价。

似乎是个定理的东西:

一个图的最小割是对偶图的最短路

question1:神马是对偶图?能吃吗?

当然不能

在这里的对偶图,就是把原来的块当做点,把原来的边建成与之垂直的边,边权不变,再在左下角和右上角新建两个点st,eend(这两个点放在哪个角上无所谓辣),作为源点和汇点,跑最短路。

为毛是eend而不是end呢?

因为在c++11下end会CE

说的太抽象了,举个例子

原图:

技术图片

对偶图:

技术图片

思路很简单,but代码还是很难(它是个要面子的题)

我们想想怎么给这些点编号

(其实随便编号)

窝的编号方法:
技术图片

接下来我们分边讨论怎么表示点(注意一定要建双向边)

横边:

技术图片

左边的红字是边的行号 i ,上边的是边的列号 j 

我们要计算每条边(i,j)上面的点和下面的点,如果边的行号是1,则直接向终点eend建边,如果边的行号是 n ,就向起点st建边,否则,上下建边(注意建双向边*2)

点的表示方法:

上面的点 ss=2*(i-1)*(m-1)+j+1; 

下面的点 ee=ss-m+1;

竖边:

 技术图片

右边的点:ss=j+(m-1)*(2*(i-1)+1)+1;

左边的点:ee=ss-m;

当j=1时:st与右边的点连边

当j=m时:左边的点与eend连边

正常情况:左边的点与右边的点连边

注意建双向边!!!(*3)

斜边:

技术图片

斜上方的点:ss=2*(i-1)*(m-1)+j+1;

斜下方的点:ee=ss+m-1;

这里就不需要考虑st和eend了

双向建边*5

建完边之后跑一遍dijkstra就好辣

Code:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long
#define pa pair<int,int>
using namespace std;
inline int read()

    char ch=getchar(),lst;
    int x=0;
    while(ch<0||ch>9)
    
        lst=ch;
        ch=getchar();
    
    while(ch>=0&&ch<=9)
    
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    return ((lst==-)?-x:x);

int n,m,st=1,eend,head[2000009],cnt,dis[2000009];
const int inf=214748364;
bool vis[2000009];
struct Ed
    int to,nxt,dis;
edge[7000009]; 
void add(int fr,int to,int dis)

    cnt++;
    edge[cnt].to=to;
    edge[cnt].dis=dis;
    edge[cnt].nxt=head[fr];
    head[fr]=cnt;

void dij()//堆优化的dij

   for(int i=1;i<=eend;i++)
    dis[i]=inf; 
   dis[1]=0; 
   priority_queue<pa,vector<pa>,greater<pa> > q;
   q.push(make_pair(0,1));
   while(!q.empty())
   
    int now=q.top().second;
     q.pop();
     if(vis[now])continue;
     vis[now]=1;
     for(int e=head[now];e;e=edge[e].nxt)
     
        int v=edge[e].to,di=edge[e].dis;
        if(dis[now]+di<dis[v])
        
            dis[v]=dis[now]+di;
            q.push(make_pair(dis[v],v));
        
     
   

int main()

    n=read();m=read();
    eend=(n-1)*2*(m-1)+2;
    for(int i=1;i<=n;i++)//横边
    
        for(int j=1;j<=m-1;j++)
        
            int dis=read();
            int ss=2*(i-1)*(m-1)+j+1; 
            int ee=ss-m+1;
            if(i==1)
                add(ss,eend,dis);add(eend,ss,dis);
                continue;
            if(i==n)
                add(st,ee,dis);add(ee,st,dis);
                continue;    
            add(ss,ee,dis);add(ee,ss,dis);
        
    //竖边
    for(int i=1;i<n;i++)
    
        for(int j=1;j<=m;j++)
        
            int dis=read();
            int ss=j+(m-1)*(2*(i-1)+1)+1;
            int ee=ss-m;
            if(j==1)
            add(st,ss,dis);add(ss,st,dis);
            continue;
            if(j==m)
            add(ee,eend,dis);add(eend,ee,dis);
            continue;
            add(ee,ss,dis);add(ss,ee,dis);
        
    //斜边
    for(int i=1;i<n;i++)
    
        for(int j=1;j<m;j++)
        
            int dis=read();
            int ss=2*(i-1)*(m-1)+j+1;
            int ee=ss+m-1;
            add(ee,ss,dis);
            add(ss,ee,dis);
        
    
    dij();
    printf("%d",dis[eend]);

 

以上是关于duliu题之狼抓兔子题解的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1001 题解

[bzoj1001]狼抓兔子

bzoj1001: [BeiJing2006]狼抓兔子

BZOJ 1001 狼抓兔子(网络流)

ICPC-Beijing 2006 狼抓兔子

BZOJ1001: [BeiJing2006]狼抓兔子