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]);
}

 

posted @ 2019-07-31 12:19  千载煜  阅读(269)  评论(0编辑  收藏  举报