拖了將近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]); }