· 对于差分约束,题目要求求什么便设什么,令$Sum[i]$表示由$0 ~ i$的雇佣人数。
· 要充分利用题目所给条件,令$Have[i]$表示i时刻申报的人数,$Need[i]$表示i时刻需要的人数「结合 “人数” 关键词」。
此时容易列出两个基本不等式:
Sum[i] - Sum[i - 1] >= 0;
Sum[i] - Sum[i - 1] <= Have[i];
此时发现还有$Need[]$和范围为8个单位长度的区间没有使用,可得:
Sum[i]
- Sum[i - 8] >= Need[i]; (i >= 8)
那么还有环形无法处理。
对于环形的处理:
将整个$24$个单位的区间以当前点为分割点,将大区间分割为$(i - 8 + 24)$ ~ $i$ (环形)以及$i$ ~ $i + 16$,相当于覆盖了整个环形区间。
相当于是去限制非当前考虑的完整的区间从而达到限制当前因环形而断开的区间。
所以此时仅需考虑两个区间间的限制即可。
并且需要一个$Sum[- 1]$。
答案显然满足单调性,故考虑二分。
令$mid$表示需要$mid$个服务员。
则可列方程:
mid - (Sum[i + 16] - Sum[i]) >= Need[i]; (i < 8)
即可解答。
· 代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 6 using namespace std; 7 8 const int MAXN = 1000 + 10; 9 const int MAXM = MAXN * 10; 10 const int MAXT = 24 + 10; 11 12 struct LinkedForwardStar { 13 int to; 14 int w; 15 16 int next; 17 } ; 18 19 LinkedForwardStar Link[MAXM << 1]; 20 int Head[MAXT]= {0}; 21 int size = 0; 22 23 void Insert (int u, int v, int w) { 24 Link[++ size].to = v; 25 Link[size].w = w; 26 Link[size].next = Head[u]; 27 28 Head[u] = size; 29 } 30 31 int T; 32 33 int N; 34 35 int Have[MAXT]; 36 int Need[MAXN]; 37 38 int Dist[MAXT]; 39 int Count[MAXT]; 40 41 queue<int> Que; 42 bool Inque[MAXT]; 43 44 bool SPFA (int mid) { 45 memset (Dist, 0xcf, sizeof (Dist)); 46 memset (Count, 0, sizeof (Count)); 47 memset (Inque, 0, sizeof (Inque)); 48 while (! Que.empty()) 49 Que.pop(); 50 51 Que.push(24); 52 Inque[24] = true; 53 Dist[24] = 0; 54 55 while (! Que.empty()) { 56 int u = Que.front(); 57 Que.pop(); 58 59 Inque[u] = false; 60 Count[u] ++; 61 62 if (Count[u] >= 25) 63 return false; 64 65 for (int i = Head[u]; i; i = Link[i].next) { 66 int v = Link[i].to, w = Link[i].w; 67 68 if (Dist[u] + w > Dist[v]) { 69 Dist[v] = Dist[u] + w; 70 71 if (! Inque[v]) { 72 Que.push(v); 73 Inque[v] = true; 74 } 75 } 76 } 77 } 78 79 return Dist[23] == mid; 80 } 81 82 bool Check (int mid) { 83 memset (Head, 0, sizeof (Head)); 84 size = 0; 85 86 Insert (24, 23, mid); 87 Insert (24, 0, 0); 88 Insert (0, 24, - Have[0]); 89 for (int i = 1; i <= 23; i ++) { 90 Insert (i - 1, i, 0); 91 Insert (i, i - 1, - Have[i]); 92 } 93 for (int i = 8; i <= 23; i ++) 94 Insert (i - 8, i, Need[i]); 95 for (int i = 0; i < 8; i ++) 96 Insert (i + 16, i, Need[i] - mid); 97 98 return SPFA (mid); 99 } 100 101 int main () { 102 scanf ("%d", & T); 103 104 for (int Case = 1; Case <= T; Case ++) { 105 memset (Have, 0, sizeof (Have)); 106 107 for (int i = 0; i <= 23; i ++) 108 scanf ("%d", & Need[i]); 109 110 scanf ("%d", & N); 111 112 for (int i = 1; i <= N; i ++) { 113 int start; 114 scanf ("%d", & start); 115 116 Have[start] ++; 117 } 118 119 bool flag = false; 120 int left = 0, right = N; 121 while (left < right) { 122 int mid = (left + right) >> 1; 123 124 if (Check (mid)) { 125 flag = true; 126 right = mid; 127 } 128 else 129 left = mid + 1; 130 } 131 132 if (! flag) 133 puts ("No Solution"); 134 else 135 printf ("%d ", left); 136 } 137 138 return 0; 139 } 140 141 /* 142 1 143 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 144 5 145 0 146 23 147 22 148 1 149 10 150 */