POJ 1275 Cashier Employment(差分约束 建模 二分)

来源:互联网 发布:菜谱软件ipad 编辑:程序博客网 时间:2024/05/21 22:48

POJ 1275

题目大意

一个商店每天24个时段各有一个需要的职工数Ri(0Ri23),有n个职工(0n1000)可以用,每个职工从一个各自的开始时间ti连续工作8小时,问最少需要多少职工能满足需要,不能满足输出无解。

分析

尝试以不同的方式去描述一个问题是着手分析的一个很好的方式,将一个问题数学化也是一种常用的方法。

  • 错误思路

    注意到职工数n最多有1000个,但是开始时间ti的情况最多只有23中,也就是说大多数职工其实是等价的,可以合并起来处理。重述一下问题:

    xi表示在时间i开始工作的员工数,由于这个时间开始工作的员工数是有限的,所以需要满足约束条件(0xiCi)。令yi表示在时段i工作的职工数,yi可以由x得出。变成形如下面的约束方程

    xa1+xa2+...+xakR0xb1+xb2+...+xbkR1...min(x0+x1+...x23)

    这种形式的整数规划是一个NP-hard问题,这个问题可以归约到0-1整数规划,但也只是这种形式的整数规划的特殊形式,这种形式的整数规划并不能还原到我们的这个问题上去,也就是说我们要求解的问题和这种形式的整数规划并不是等价的。因此不能从这种形式的整数规划的普遍解法的角度考虑这个问题,可以通过观察这个约束方程的特征希望能够找到优化的方法是一个思路。

    我们应该注意到一个规律就是:如果将x按照职工开始工作的时间进行编号的话,那么约束方程的每一个约束不等式的编号将是连续的,并且每个不等式的变量个数是相同的。

    也就是说不等式是形如:

    x17+x18+...+x23+x0R0x18+...+x23+x0+x1R1...x16+x17+...+x22+x23R23min(x1+x2+...x23)

    根据这条启发式信息,既然每个不等式的变量数相同都为8且具有相同的7项,那么可以通过两式相减就变成形如abc的经典差分约束不等式了:

    x17x1R0R1x18x2R1R2...x15x23R22R23x16x0R23R0

    要求min(x0+x1+...+x23)

    我们可以新建一个源点x24=0

    再加上每个变量满足(0xici)条件:

    {x24xiCixix240

    但是这样是没法求出min(x0+x1+...+x23)

    然后就卡在这里了!!!

  • 再分析

    想不出思路后,百度,在这里继续写一下分析的过程:

    既然将x0,x1...x23分开求是得不到它们和的最小值的,那么我们可以用一个变量si表示时刻i及之前实际开始工作的总职工数。那么有(s1=0)

    0sisi1Cisis(i8)Risi+s23si+16Ri0i238i230i7

    转化成标准形式后就是(下标做了+1处理):
    sisi10si1siCisis(i8)Risis(i+16)Ris24s24s0s24min(s24)1i241i248i231i7s24ans

    由于上面第四个不等式由三个变量,但s24比较特殊,所以可以用二分的方法对每一个s24判断是否可行。

总结

其实这道题也不是特别难,下标处理上稍微复杂了一点,一开始思路走错了没有想到先区间求和的处理。

又是一道弄了一天的题。。。不过就是要多做点这样的题才能使自己的能力提升。
看见博客有用单纯形法求解线性规划的,虽然效率低一点了,但很通用啊,有空学习一下:POJ1275 Cashier Employment[差分约束系统 || 单纯形法]

代码

#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<cstdlib>#include<queue>#include<map>#include<algorithm>#include<set>using namespace std;const int INF=0x7fffffff;int T;int R[25];int n;int C[25];//C[i]表示以i作为开始时间的时段的职工数目int s[25];struct Edge{      int to;      int next;      int w;}edge[201005];int edgecount;int flag;//flag=0表示无环int head[1005];int dis[1005];int vis[1005];int in[1005];//表示某个点进队的次数void Init(){      memset(head,-1,sizeof(head));      memset(vis,0,sizeof(vis));      memset(in,0,sizeof(in));      edgecount=0;      flag=0;}void Add_edge(int u,int v,int w){      edge[++edgecount].to=v;      edge[edgecount].w=w;      edge[edgecount].next=head[u];      head[u]=edgecount;}void Spfa(int s,int ans)//无解flag=1{      for(int i=0;i<=24;i++)dis[i]=-INF;      memset(vis,0,sizeof(vis));      memset(in,0,sizeof(in));      dis[s]=0;      vis[s]=1;//vis为1表示在队列里面      queue<int>Q;      Q.push(s);      while(!Q.empty())      {           int u=Q.front();           Q.pop();           for(int k=head[u];k!=-1;k=edge[k].next)           {                 int t=edge[k].w+dis[u];                 if(dis[edge[k].to]<t)                 {                       dis[edge[k].to]=t;                       if(vis[edge[k].to]!=1)                       {                           Q.push(edge[k].to);                           vis[edge[k].to]=1;                           in[edge[k].to]++;                       }                       if(in[edge[k].to]>24){flag=1;return ;}                 }           }           vis[u]=0;      }      if(dis[24]!=ans)flag=1;}void Build(int ans){    int sum=0;    for(int i=1;i<=24;i++)    {        Add_edge(i,i-1,-C[i]);        Add_edge(i-1,i,0);    }    for(int i=9;i<=24;i++)          Add_edge(i-8,i,R[i]);    for(int i=1;i<=8;i++)          Add_edge(i+16,i,R[i]-ans);    Add_edge(0,24,ans);}void Solve()//二分{    int L=0;    int R=n;    int M;    int ans=-1;    while(L<R)    {          M=(L+R)/2;          Init();          Build(M);          Spfa(0,M);          if(flag==0)          {              ans=M;              R=M;          }          else L=M+1;    }    if(ans!=-1){cout<<ans<<endl;}    else cout<<"No Solution"<<endl;}int main(){    scanf("%d",&T);    int t;    int ans;    while(T--)    {          ans=0;          memset(C,0,sizeof(C));          for(int i=1;i<=24;i++)scanf("%d",&R[i]);          scanf("%d",&n);          for(int i=1;i<=n;i++)          {              scanf("%d",&t);              C[t+1]++;          }          Solve();    }    return 0;}
0 0