平面图网络流,比赛的时候想不到怎么建对偶图试了试dinic,居然水过去了。。数据是真水。。
赛后看了各牛的解题报告,才搞懂怎么建对偶图,确实是很巧妙。关于对偶图的概念,可以去看08年周东的论文《两极相通——浅析最大—最小定理在信息学竞赛中的应用》。
建对偶图的难点在于确定每条边相邻的是哪两个块,过程可分为四步
1、将无向图的每条边拆为两条有向边,然后扫描所有的点,将某边对于该点的入边ID和出边ID以及极角存起来。注意起点和终点之间也要连一条边。
2、将与该点相连的边按照极角排序,每条边的入边ID的next指向下一条边的出边ID。不难发现,这样操作以后,每个块周围的边形成了一个环,并且一条无向边的两条正向边属于相邻的两个块。
3、扫描一遍所有的边,通过next数组可以标记出哪些边属于同一个块。
4、一条无向边的两条有向边属于相邻的两个块,建立对偶图,用最短路求解。
1 #include <string.h> 2 #include <stdio.h> 3 #include <vector> 4 #include <algorithm> 5 #include <queue> 6 #include <math.h> 7 #define INF 0x3fffffff 8 #define MAXN 100005 9 using namespace std; 10 const double PI=acos(-1.0); 11 struct pnt{int x,y;}p[MAXN]; 12 struct edge{int v,w,n;}e[MAXN*2]; 13 double eps=1e-8; 14 inline int dcmp(const double &x){return (x>eps)-(x<-eps);} 15 struct v_edge{ 16 int in,out; 17 double arg; 18 v_edge(int f,int b,double args):in(f),out(b),arg(args){}; 19 bool operator <(const v_edge& e)const{ 20 return dcmp(arg-e.arg)==-1; 21 } 22 }; 23 vector<v_edge> vec[MAXN]; 24 int cas,n,m,s,t,tu,tv,es; 25 int cap[MAXN],first[MAXN],ess;; 26 int next[MAXN*2],col[MAXN*2],cols; 27 inline void addedge(int &u,int &v,int &w){ 28 e[ess].v=v,e[ess].w=w,e[ess].n=first[u],first[u]=ess++; 29 } 30 void makegraph(){ 31 es=ess=0; 32 for(int i=1;i<=n;i++)vec[i].clear(); 33 //读入边并建图,存极角以便按极角排序 34 for(int i=0;i<m;i++){ 35 scanf("%d%d%d",&tu,&tv,&cap[i]); 36 if(tu==tv)continue; 37 double argv=atan2((double)p[tv].y-p[tu].y,(double)p[tv].x-p[tu].x); 38 double argu=atan2((double)p[tu].y-p[tv].y,(double)p[tu].x-p[tv].x); 39 vec[tu].push_back(v_edge(es<<1,es<<1|1,argv)); 40 vec[tv].push_back(v_edge(es<<1|1,es<<1,argu)); 41 es++; 42 } 43 //起点和终点之间要加一条边并保证在最外面 44 cap[es]=INF; 45 vec[s].push_back(v_edge(es<<1,es<<1|1,PI)); 46 vec[t].push_back(v_edge(es<<1|1,es<<1,0)); 47 es++; 48 //求出每条边按逆时针方向的next边 49 for(int i=1;i<=n;i++){ 50 int size=vec[i].size(); 51 sort(vec[i].begin(),vec[i].end()); 52 for(int j=0;j<size-1;j++)next[vec[i][j].in]=vec[i][j+1].out; 53 next[vec[i][size-1].in]=vec[i][0].out; 54 } 55 //将边分到对应的块中 56 memset(col,-1,es*8);cols=0; 57 for(int i=0;i<es*2;i++){ 58 if(col[i]!=-1)continue; 59 ++cols; 60 int tt=i; 61 for(tt=i;next[tt]!=i;tt=next[tt])col[tt]=cols; 62 col[tt]=cols; 63 } 64 //建立对偶图 65 memset(first,-1,(cols+1)*4); 66 for(int i=0;i<es;i++){ 67 addedge(col[i<<1],col[i<<1|1],cap[i]); 68 addedge(col[i<<1|1],col[i<<1],cap[i]); 69 } 70 } 71 //---dijkstra--- 72 typedef pair<int,int> pii; 73 int d[MAXN]; 74 bool cal[MAXN]; 75 int dijkstra(int st,int en,int totn){ 76 priority_queue<pii,vector<pii>,greater<pii> > q; 77 for(int i=0;i<=totn;i++){ 78 d[i]=(i==st?0:INF); 79 cal[i]=0; 80 } 81 q.push(make_pair(d[st],st)); 82 while(!q.empty()){ 83 pii pi=q.top();q.pop(); 84 int u=pi.second; 85 if(u==en)return d[en]; 86 if(cal[u])continue; 87 cal[u]=true; 88 for(int i=first[u];i!=-1;i=e[i].n){ 89 int v=e[i].v; 90 if(d[v]>d[u]+e[i].w){ 91 d[v]=d[u]+e[i].w; 92 q.push(make_pair(d[v],v)); 93 } 94 } 95 } 96 return d[en]; 97 } 98 int main(){ 99 for(scanf("%d",&cas);cas--;){ 100 scanf("%d%d",&n,&m); 101 s=t=1; 102 for(int i=1;i<=n;i++){ 103 scanf("%d%d",&p[i].x,&p[i].y); 104 if(p[i].x<p[s].x)s=i; 105 if(p[i].x>p[t].x)t=i; 106 } 107 makegraph(); 108 int ans=dijkstra(col[(es-1)<<1|1],col[(es-1)<<1],cols); 109 printf("%d\n",ans); 110 } 111 return 0; 112 }