HDU 4280 Island Transport [平面图网络流]

  平面图网络流,比赛的时候想不到怎么建对偶图试了试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 }
原文地址:https://www.cnblogs.com/swm8023/p/2684215.html