duliu題之狼抓兔子題解

拖了將近5天的正解和AC.........emmmmm...........c++

事實告訴咱們這種毒瘤題必定要建雙向邊(用了不知道多少個小時質疑建邊的人慾哭無淚)優化

心態爆炸的傳送spa

題了個面c++11

這是個求最小割問題code

說人話:blog

 把圖中的一些邊砍斷,使這個圖分爲不連通的兩部分。砍斷一條邊的代價就是這條邊的邊權,求最小代價。get

彷佛是個定理的東西:string

一個圖的最小割是對偶圖的最短路it

question1:神馬是對偶圖?能吃嗎?io

固然不能

在這裏的對偶圖,就是把原來的塊當作點,把原來的邊建成與之垂直的邊,邊權不變,再在左下角和右上角新建兩個點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]);
}
相關文章
相關標籤/搜索