1. 程式人生 > >【LA3126 訓練指南】出租車 【DAG最小路徑覆蓋】

【LA3126 訓練指南】出租車 【DAG最小路徑覆蓋】

位置 flow 模型 play nic pen 所在 dag algorithm

題意

你在一座城市裏負責一個大型活動的接待工作。明天將有m位客人從城市的不同的位置出發,到達他們各自的目的地。已知每個人的出發時間,出發地點和目的地。你的任務是用盡量少的出租車送他們,使得每次出租車接客人時,至少能提前一分鐘到達他所在的位置。註意,為了滿足這一條件,要麽這位客人是這輛出租車接送的第一個人,要麽在接送完上一個客人後,有足夠的時間從上一個目的地開到這裏。

為了簡單起見,假定城區是網格型的,地址用坐標(x,y)表示,出租車從(x1,y1)到(x2,y2)處需要行駛|x1-x2|+|y1-y2|分鐘。

分析

這個模型叫做DAG的最小路徑覆蓋。所謂最小路徑覆蓋,就是在圖中找盡量少的路徑,使得每個結點恰好在一條路徑上(換句話說,不同的路徑不能有公共點)。註意,單獨的一個結點也可以作為一條路徑。

DAG最小路徑覆蓋的解法如下:把所有的結點i拆為X結點i和Y結點i‘,如果圖G中存在有向邊i->j,則二分圖中引入邊i->j‘。設二分圖的最大匹配數為m,則結果就是n-m。因為匹配和路徑覆蓋是一一對應的。對於路徑覆蓋中的每條簡單路徑,除了最後一個“結尾結點”以外都有唯一的後繼和他對應(既匹配結點),因此匹配數就是非結尾結點的個數,當匹配數達到最大時,此時,結尾結點的個數最少,既路徑條數最少。

本題建模:每個客人是一個結點,如果同一個出租車接完客人u以後還來得及接客人v,連邊u->v。不難發現,這個圖是一個DAG,並且它的最小路徑覆蓋就是本題的答案。

技術分享圖片
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #include <algorithm>
  5 #include <queue>
  6 #include <cmath>
  7 
  8 using namespace std;
  9 const int maxn=3000+10;
 10 const int maxm=800000;
 11 const int INF=2147483600;
 12 struct
Dinic{ 13 int head[maxn],Next[maxm],to[maxm],cap[maxm],flow[maxm]; 14 int sz,n,m,s,t; 15 bool vis[maxn]; 16 int cur[maxn],d[maxn]; 17 void init(int n){ 18 this->n=n; 19 memset(head,-1,sizeof(head)); 20 this->sz=-1; 21 } 22 void add_edge(int a,int b,int c){ 23 ++sz; 24 to[sz]=b; 25 cap[sz]=c;flow[sz]=0; 26 Next[sz]=head[a];head[a]=sz; 27 ++sz; 28 to[sz]=a; 29 cap[sz]=c;flow[sz]=c; 30 Next[sz]=head[b];head[b]=sz; 31 } 32 bool BFS(){ 33 memset(vis,0,sizeof(vis)); 34 queue<int>Q; 35 vis[s]=1; 36 d[s]=0; 37 Q.push(s); 38 while(!Q.empty()){ 39 int u=Q.front();Q.pop(); 40 for(int i=head[u];i!=-1;i=Next[i]){ 41 int v=to[i]; 42 if(!vis[v]&&cap[i]>flow[i]){ 43 vis[v]=1; 44 d[v]=d[u]+1; 45 Q.push(v); 46 } 47 } 48 } 49 return vis[t]; 50 } 51 int DFS(int x,int a){ 52 if(x==t||a==0)return a; 53 int Flow=0,f; 54 for(int& i=cur[x];i!=-1;i=Next[i]){ 55 int v=to[i]; 56 if(d[v]==d[x]+1&&(f=DFS(v,min(a,cap[i]-flow[i])))>0){ 57 Flow+=f; 58 flow[i]+=f; 59 flow[i^1]-=f; 60 a-=f; 61 if(a==0)break; 62 } 63 } 64 return Flow; 65 } 66 int Maxflow(int s,int t){ 67 this->s=s,this->t=t; 68 int Flow=0; 69 while(BFS()){ 70 for(int i=0;i<=n;i++) 71 cur[i]=head[i]; 72 73 Flow+=DFS(s,INF); 74 } 75 return Flow; 76 } 77 }dinic; 78 int T,m; 79 int sth[maxn],stm[maxn],sx[maxn],sy[maxn],enx[maxn],eny[maxn]; 80 int dist(int x1,int y1,int x2,int y2){ 81 return abs(x1-x2)+abs(y1-y2); 82 } 83 void pass_time(int hou,int mi,int &Hou,int &Mi,int tim){ 84 Mi=mi+tim; 85 Hou=hou+Mi/60; 86 Mi=Mi%60; 87 return; 88 } 89 int main(){ 90 scanf("%d",&T); 91 for(int t=1;t<=T;t++){ 92 scanf("%d",&m); 93 dinic.init(2*m+5); 94 for(int i=1;i<=m;i++){ 95 scanf("%d:%d%d%d%d%d",&sth[i],&stm[i],&sx[i],&sy[i],&enx[i],&eny[i]); 96 } 97 98 for(int i=1;i<=m;i++){ 99 for(int j=1;j<=m;j++){ 100 int tim=dist(sx[i],sy[i],enx[i],eny[i])+dist(enx[i],eny[i],sx[j],sy[j]); 101 int Enh,Enm; 102 pass_time(sth[i],stm[i],Enh,Enm,tim); 103 if(Enh*60+Enm>=sth[j]*60+stm[j])continue; 104 dinic.add_edge(i,j+m,1); 105 } 106 } 107 for(int i=1;i<=m;i++) 108 dinic.add_edge(0,i,1); 109 for(int i=1;i<=m;i++) 110 dinic.add_edge(i+m,2*m+1,1); 111 int ans=dinic.Maxflow(0,2*m+1); 112 printf("%d\n",m-ans); 113 114 } 115 return 0; 116 }
View Code

【LA3126 訓練指南】出租車 【DAG最小路徑覆蓋】