AcWing 393. 雇佣收银员

原题链接

考察:差分约束+二分+前缀和

思路:

       某个区间有多少个,考虑前缀和.

       那么:   s[i] - s[i-1] >= 0 , s[i] - s[i-1] 表示第i小时雇佣的人,s[i] - s[i-1] <= sum[i] sum[i]表示可以在i时刻开始工作的人数.

       注意r[i]表示第i小时需要的人数,第i小时可以被i-7~i覆盖. 所以 s[i] - s[i-8] >= r[i].

       如果i>=8 可以是上面的式子,但是当i<8 式子就变成 s[i] +s[24] - s[i+16] >= r[i]

       最后一个不等式有三个变量,不能简单的差分约束.一个简单的方法就是枚举s[24], 这明显有单调性,直接二分即可.

       注意,为了在不等式里凸显s[24] = mid 需要 s[24] <=mid && s[24] >=mid;

 1 #include <iostream> 
 2 #include <cstring>
 3 #include <stack>
 4 using namespace std;
 5 const int N = 30,M = 120;
 6 int r[N],n,sum[N],idx,h[N],cnt[N],dist[N];
 7 bool st[N];
 8 struct Road{
 9     int fr,to,ne,w;
10 }road[M];
11 void add(int a,int b,int c)
12 {
13     road[idx].w = c,road[idx].fr = a,road[idx].to = b,road[idx].ne = h[a],h[a] = idx++;
14 }
15 bool spfa(int m)
16 {
17     idx = 0;
18     memset(h,-1,sizeof h); memset(cnt,0,sizeof cnt);
19     memset(st,0,sizeof st); memset(dist,0,sizeof dist);
20     for(int i=1;i<=24;i++) dist[i] = -0x3f3f3f3f;
21     for(int i=8;i<=24;i++) add(i-8,i,r[i]);
22     for(int i=1;i<=24;i++) add(i-1,i,0);
23     for(int i=1;i<=24;i++) add(i,i-1,-sum[i]);
24     for(int i=1;i<8;i++) add(16+i,i,r[i]-m);
25     add(0,24,m); add(24,0,-m);
26     stack<int> q;
27     q.push(0); st[0] = 1;
28     while(q.size())
29     {
30         int u = q.top();
31         q.pop();
32         st[u] =0;
33         for(int i=h[u];~i;i=road[i].ne)
34         {
35             int v = road[i].to;
36             if(dist[v]<dist[u]+road[i].w)
37             {
38                 dist[v] = dist[u]+road[i].w;
39                 cnt[v] = cnt[u]+1;
40                 if(cnt[v]>=25) return 0; 
41                 if(!st[v]) q.push(v),st[v] = 1;
42             }
43         }
44     }
45     return 1;
46 }
47 int main()
48 {
49 //    freopen("in.txt","r",stdin);
50     int T;
51     scanf("%d",&T);
52     while(T--)
53     {
54         memset(sum,0,sizeof sum);
55         for(int i=1;i<=24;i++) scanf("%d",&r[i]);
56         scanf("%d",&n);
57         for(int i=1;i<=n;i++)
58         {
59             int x; scanf("%d",&x);
60             sum[x+1]++;
61         }
62         int l = 0,r = n+1;
63         while(l<r)
64         {
65             int mid = l+r>>1;
66             if(spfa(mid)) r = mid;
67             else l = mid+1;
68         }
69         if(r==n+1) puts("No Solution");
70         else printf("%d\n",r);
71     }
72     return 0;
73 }

 

 

关于本题的一些细节:

       为什么不能以24为起点,因为这样推出来的dist[0]不一定为0,这样就没有实际意义.

posted @ 2021-05-08 23:59  acmloser  阅读(42)  评论(0编辑  收藏  举报