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;}
新闻热点
疑难解答