NOIP2011Day2第三题观光公交的费用流解法

来源:互联网 发布:淘宝上标企是什么意思 编辑:程序博客网 时间:2024/04/28 23:48
  风景迷人的小城Y 市,拥有n 个美丽的景点。由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第 0 分钟出现在 1号景点,随后依次前往 2、3 、4 ……n 号景点。从第 i 号景点开到第 i+1 号景点需要 Di 分钟。
任意时刻,公交车只能往前开,或在景点处等待。
  设共有m 个游客,每位游客需要乘车1 次从一个景点到达另一个景点,第i 位游客在Ti 分钟来到景点 Ai ,希望乘车前往景点Bi (Ai<Bi )。为了使所有乘客都能顺利到达目的地,公交车在每站都必须等待需要从该景点出发的所有乘客都上车后才能出发开往下一景点。假设乘客上下车不需要时间。 
  一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司机ZZ给公交车安装了 k 个氮气加速器,每使用一个加速器,可以使其中一个 Di 减1 。对于
同一个Di 可以重复使用加速器,但是必须保证使用后Di 大于等于0 。
  那么ZZ该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?
——————————————分割线—————————————————————————————————————————————
在TYVJ的首页上发现有人询问这道题的费用流解法,于是就自己想了一下........
所求:最小化sigma(travel[i]) i∈[1,m]
我们发现travel[i]=arrive[i]-set_off[i]
而且对于同一个车站下车的旅客i,j,arrive[i]=arrive[j]
设t[i]为公交车在i车站的到达时间,f[i]为公交车在i车站下车的人数,于是,这个所求就变成了:
最小化sigma(t[i]*f[i])-sigma(set_off[j]) i∈[1,n],j∈[1,m]
减号后面的式子是已知量,把它忽略于是就变成了最小化sigma(t[i]*f[i])
然后就是写线性规划的约束条件了
设last[i]为i车站最后到达的人来的时刻,dist[i]为i-1到i的距离,leave[i]为公交车离开i车站的时间
因为这里的费用是点费用,所以需要拆点为i到达和i出发两个点,连边inf,费用为f[i]
我们知道t[i]=max(t[i-1],last[i-1])+dist[i],leave[i]=max(t[i],last[i])
考虑i到达这个点
改写表达式
t[i]-last[i-1]-dist[i]≥0
当这个式子大于0的时候,有t[i]=t[i-1]+dist[i],设delta[i]=t[i]-last[i]
于是delta[i-1]=t[i]-last[i-1]-dist[i],delta[i-1]>0
那么就有delta[i-1]+last[i-1]+dist[i]-t[i]=0
下面考虑i出发这个点
如果leave[i]>last[i],那么一定就有t[i]>last[i],即t[i]-last[i]-delta[i]=0
于是对于点i,我们就可以列出如下的线性规划的约束条件
delta[i-1]+last[i-1]+dist[i]-t[i]=0,t[i]-last[i]-delta[i]=0
想到NOI2008年的志愿者招募,我们可以考虑如下建图
从源点到i点连容量last[i-1]+dist[i],费用为0的边,从(i-1)'到i连一条容量Inf,费用为0的边,从i点到i'点连容量为Inf,费用为f[i]的边,再从i'点到汇点连一条容量为last[i],费用为0的边
当我们再把t[i]-last[i]>0这个条件去除后,发现这个建图依然满足条件,于是这个图我们就建成了
等等!
没有考虑氮气加速器......
再考虑了氮气加速器的时候,用a[i]表示到达i点的时候使用了a[i]次加速器,那么sigma(a[i])=k,a[i]<=d[i]
于是原约束条件就变成
delta[i-1]+last[i-1]+dist[i]-t[i]-a[i]=0
就想到再从i点向汇点连一条容量为d[i]的边,但是如此就无法体现sigma(a[i])=k的限制...........
呃,想了好久,我们就再设一个t'点,从i点向t'点连一条容量为d[i]的边,再从t'到汇点连一条容量为k的边,如此便解决了。
接下来就是对这个图跑一次最小费用最大流。
呃................还是得贴代码
不过贴代码之前贴个图,比竟这么长都是自己列式而且是正确的,不容易啊
 

 
#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#define min(a,b) (a<b)?a:b#define inf 0x7fffffffusing namespace std;pair<int,int> c[50010];int v[2010],d[2010],cur[2010],e[50010],next[50010];int f[2010],last[2010],dist[2010];int n,m,k,s=0,tt,t,cnt=1;bool hash[2010];inline void connect(int x,int y,int flow,int cost){ next[++cnt]=v[x],v[x]=cnt,e[cnt]=y,c[cnt]=make_pair(flow,cost); next[++cnt]=v[y],v[y]=cnt,e[cnt]=x,c[cnt]=make_pair(0,-cost);}int aug(int r,int cp){ if (r==t)  return cp; hash[r]=true; for (int temp=cur[r];temp;temp=next[temp])  {   int j=e[temp];   if (!hash[j] && d[r]==d[j]+c[temp].second && c[temp].first>0)    if (int tmp=aug(j,min(cp,c[temp].first)))     return c[temp].first-=tmp,c[temp^1].first+=tmp,cur[r]=temp,tmp;  } return 0;}inline bool modlabel(){ int tmp=inf; for (int i=s;i<=t;i++)  if (hash[i])   for (int temp=v[i];temp;temp=next[temp])    if (!hash[e[temp]] && c[temp].first>0)     tmp=min(tmp,d[e[temp]]+c[temp].second-d[i]); if (tmp==inf)  return true; for (int i=s;i<=t;i++)  if (hash[i])   hash[i]=false,d[i]+=tmp; return false;}inline int zkw(){ int ans=0; while (1)  {   for (int i=s;i<=t;i++)    cur[i]=v[i];   while (int tmp=aug(s,inf))    {     ans+=tmp*d[s];     memset(hash,0,sizeof(hash));    }   if (modlabel())    break;  } return ans;}int main(){ freopen("bus.in","r",stdin); freopen("bus.out","w",stdout); scanf("%d%d%d",&n,&m,&k); for (int i=2;i<=n;i++)  scanf("%d",dist+i); int ti,ai,bi,temp=0; for (int i=1;i<=m;i++)  {   scanf("%d%d%d",&ti,&ai,&bi);   if (ti>last[ai])    last[ai]=ti;   f[bi]++,temp+=ti;  } tt=n+n+1,t=n+n+2; for (int i=1;i<=n;i++)  {   if (i!=1)    connect(s,i,last[i-1]+dist[i],0),connect(i+n,t,last[i],0),connect(i,tt,dist[i],0);   if (i!=n)    connect(i+n,i+1,inf,0);   connect(i,i+n,inf,f[i]);  } connect(n+n,t,inf,0),connect(tt,t,k,0); int ans=zkw(); printf("%d\n",ans-temp); return 0;} 

原创粉丝点击