【POJ 1275】 Cashier Employment(差分约束系统的建立和求解)
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 7569 | Accepted: 2856 |
Description
Input
Output
Sample Input
1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 5 0 23 22 1 10
Sample Output
1
Source
做到现在为止 做的最麻烦的一个差分约束……
麻烦不是麻烦在求解上,而是建图……并且绝大多数差分约束的难点,都是建图。。毕竟求解就是个裸最短路
这题题意是 一个商店要招聘员工 商店有一个客流表 表示0~23点每点需要的最少员工数
之后一个整数n 表示有n个应聘员工
后面n行 每行一个0~23的整数 表示第i个员工工作的起始时间 每个员工工作8小时
题目分析完毕 条件不多 所以这就是这题的难点……要挖掘很多隐含条件
设t[i]为商店第i小时需要的员工数 r[i]表示第i小时来应聘的员工数 数组S[i]表示0~i小时总共应聘的员工数
第一个条件 0 <= S[i]-S[i-1] <= r[i]
第二个条件 S[i]-S[i-8] >= t[i] (i-1时i-8招聘的员工刚好下班 因此S[i]-S[i-8]即为第i小时招聘的全部员工)
第三个条件 S[24]-S[0] <= sum(由于要找最少的总招聘人数,所以这里出现了一个未知量,而这个问题就转化成了求最小sum)
把三个问题化成同号 再细化一下 就变成了
S[i-1]-S[i] <= 0
S[i]-S[i-1] <= r[i]
S[i-8]-S[i] <= -t[i](0 < i <= 8)
S[i+16]-S[i] <= sum-t[i](8 < i <= 24)
S[24]-S[0] <= sum
找到不等关系就好办一点了 对sum二分 每次更新与sum有关的边权 然后跑最短路 判断一下能不能跑出来(有无负环)
代码如下:
#include <iostream> #include <cmath> #include <vector> #include <cstdlib> #include <cstdio> #include <cstring> #include <queue> #include <list> #include <algorithm> #include <map> #include <set> #define LL long long #define Pr pair<int,int> #define fread() freopen("in.in","r",stdin) #define fwrite() freopen("out.out","w",stdout) using namespace std; const int INF = 0x3f3f3f3f; const int msz = 10000; const double eps = 1e-8; struct Edge { int u,v,w; }; Edge eg[233333]; int w[25],r[25]; int dis[25]; int tp; bool bellman() { memset(dis,INF,sizeof(dis)); dis[0] = 0; for(int i = 0; i <= 24; ++i) for(int j = 0; j < tp; ++j) if(dis[eg[j].v] > dis[eg[j].u]+eg[j].w) dis[eg[j].v] = dis[eg[j].u]+eg[j].w; //判负环 for(int j = 0; j < tp; ++j) if(dis[eg[j].v] > dis[eg[j].u]+eg[j].w) return false; return true; } void Add(int u,int v,int w) { eg[tp].u = u; eg[tp].v = v; eg[tp++].w = w; } //重建与sum有关的边 void buildgraph(int sum) { tp = 64; for(int i = 1; i < 9; ++i) Add(i,i+16,sum-w[i]); Add(24,0,-sum); } int main() { int t,x,n,low,high; scanf("%d",&t); while(t--) { tp = 0; high = 0; for(int i = 1; i <= 24; ++i) scanf("%d",&w[i]); memset(r,0,sizeof(r)); scanf("%d",&n); high = n; while(n--) { scanf("%d",&x); r[x+1]++; } //边权不变的边(与sum无关的边) for(int i = 1; i <= 24; ++i) { Add(i-1,i,r[i]); Add(i,i-1,0); if(i >= 9) Add(i,i-8,-w[i]); } low = 0; int ans = INF; while(low <= high) { int mid = (low+high)>>1; buildgraph(mid); if(bellman()) { ans = mid; high = mid-1; }else low = mid+1; } if(ans == INF) puts("No Solution"); else printf("%d\n",ans); } return 0; }