PS:這是get姿勢后的第一道建圖稍微麻煩的題,居然寫完代碼沒調試一次AC了~~~哈哈~~~~
2 31 9 41 8 76 2 37 5 4 86 2 8 710 4 1 7 5 35 4 10 2 1 96 3 2 9 5 38 9 6 3 10 10 Sample Output18 Source2009 Multi-University Training Contest 13 - Host by HIT題目思路:
s-t平面圖最小割轉最短路的裸題,具體可以參見我之前寫的博客: s-t平面圖最小割轉最短路算法
這題我們很容易可以得到對偶圖的點的個數為n*m*4+2 ,邊的個數為n*m*4+n*m*2+n+m-2,
所以如果用普通的spfa的話會超時,因此我們這里要用優先隊列優化,這里編號的話我們可以
以0點為S*,n*m*4+1為T*,然后中間每個大格子中的四個小格子編號可以1 2 3 4 這樣按順序排
從上到下,從左到右! 注意下細節,應改建起圖還是不難的!
AC代碼:
#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<queue>using namespace std;const int maxn = 1e6+100;const int inf = 1e9;struct nod{ int v,w,nex;}edge[maxn<<2];int n,m,nn,e,k;int dis[maxn],hed[maxn];bool vis[maxn];void add(int u,int v,int w){ edge[e].v = v,edge[e].w = w,edge[e].nex = hed[u],hed[u]=e++; edge[e].v = u,edge[e].w = w,edge[e].nex = hed[v],hed[v]=e++;}void dij(){ for(int i=0;i<=nn;i++) dis[i]=inf,vis[i]=0; dis[0]=0; priority_queue<pair<int,int > >q; q.push(make_pair(-dis[0],0)); while(!q.empty()){ int u = q.top().second;q.pop(); if(vis[u])continue;vis[u]=1; for(int i=hed[u];~i;i=edge[i].nex){ int v = edge[i].v; if(dis[v]>dis[u]+edge[i].w){ dis[v]=dis[u]+edge[i].w; if(!vis[v]){ q.push(make_pair(-dis[v],v)); } } } }}int main(){ while(~scanf("%d%d",&n,&m)){ nn = n*m*4+1;e=0; memset(hed,-1,sizeof(hed)); for(int i=1;i<=n+1;i++){ for(int j=1;j<=m;j++){ scanf("%d",&k); if(i==1) //最上面的邊 add(nn,(j-1)*4+1,k); else if(i==n+1) //最下面的邊 add(0,(n-1)*m*4+(j-1)*4+3,k); else //中間水平的邊 add((i-2)*m*4+(j-1)*4+3,(i-1)*m*4+(j-1)*4+1,k); } } for(int i=1;i<=n;i++){ for(int j=1;j<=m+1;j++){ scanf("%d",&k); if(j==1) //最左面的邊 add(0,(i-1)*m*4+2,k); else if(j==m+1) //最右面的邊 add(nn,i*m*4,k); else //中間垂直的邊 add((i-1)*m*4+(j-1)*4,(i-1)*m*4+(j-1)*4+2,k); } } for(int i=0;i<2*n;i++){ for(int j=0;j<2*m;j++){ scanf("%d",&k); //中間斜著的邊,這個公式在圖上畫畫應該不難推 add(i/2*m*4+j/2*4+1+(i%2)*2,i/2*m*4+j/2*4+2+(j%2)*2,k); } } dij(); printf("%d/n",dis[nn]); } return 0;}
新聞熱點
疑難解答