hdu1529 Cashier Employment[差分约束+二分答案]
这题是一个类似于区间选点,但是有一些不等式有三个未知量参与的情况。
依题意,套路性的,将小时数向右平移1个单位后,设$f_i$为前$i$小时工作的人数最少是多少,$f_{24}$即为所求。设$c_i$为第$i$小时可选人数,$lim_i$为要求人数下限。
- $0\le f_i-f_{i-1}\le c_i$,保证合法
- $i\ge 8$时,$f_i-f_{i-8}\ge lim_i$
- $i<8$时,由于环形,$f_i+(f_{24}-f_{i+16})\ge lim_i$,但这个不好处理,因为这不是一个二元关系,有第三个未知数参与。不过分析可知这些不等式中参与的第三个量是同一个,那么我们可以暴力枚举这个$f_{24}$,然后化为二元,再建图看合不合法。
- 使用上一条的方法时,注意限制$f_{24}-f_0=枚举的数$
事实上,答案满足可二分性,于是改为二分答案,跑正环检验即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #define dbg(x) cerr << #x << " = " << x <<endl 8 #define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl 9 using namespace std; 10 typedef long long ll; 11 typedef double db; 12 typedef pair<int,int> pii; 13 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 14 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 15 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;} 16 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;} 17 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;} 18 template<typename T>inline T read(T&x){ 19 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 20 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 21 } 22 const int N=1000,INF=0x3f3f3f3f; 23 struct thxorz{int to,nxt,w;}G[N]; 24 int Head[26],tot; 25 int cnt[26],lim[26]; 26 int T,n,L,R; 27 inline void Addedge(int x,int y,int z){G[++tot].to=y,G[tot].nxt=Head[x],Head[x]=tot,G[tot].w=z;} 28 int dis[26],inq[26],rel[26]; 29 #define y G[j].to 30 inline bool spfa(){ 31 queue<int> q; 32 for(register int i=0;i<=24;++i)q.push(i),dis[i]=0,inq[i]=1,rel[i]=0; 33 while(!q.empty()){ 34 int x=q.front();q.pop(); 35 inq[x]=0;//dbg(x); 36 for(register int j=Head[x];j;j=G[j].nxt)if(MAX(dis[y],dis[x]+G[j].w)){ 37 rel[y]=rel[x]+1;if(rel[y]>=25)return 0;//dbg(y); 38 if(!inq[y])inq[y]=1,q.push(y); 39 } 40 } 41 return 1; 42 } 43 #undef y 44 inline bool check(int mid){ 45 memset(Head,0,sizeof Head),tot=0; 46 for(register int i=1;i<=24;++i)Addedge(i-1,i,0),Addedge(i,i-1,-cnt[i]); 47 for(register int i=8;i<=24;++i)Addedge(i-8,i,lim[i]); 48 for(register int i=1;i<8;++i)Addedge(i+16,i,lim[i]-mid); 49 Addedge(0,24,mid),Addedge(24,0,-mid); 50 return spfa(); 51 } 52 53 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout); 54 read(T);while(T--){ 55 for(register int i=1;i<=24;++i)read(lim[i]); 56 memset(cnt,0,sizeof cnt); 57 read(n);for(register int i=1,x;i<=n;++i)read(x),cnt[x+1]++; 58 L=0,R=n+1;//dbg(check(1)),dbg(check(2)),dbg(check(3)),dbg(check(4)),dbg(check(5)); 59 while(L<R){ 60 int mid=L+R>>1; 61 if(check(mid))R=mid; 62 else L=mid+1; 63 } 64 if(L<=n)printf("%d\n",L); 65 else puts("No Solution"); 66 } 67 return 0; 68 }
一道弱智小题耗我半小时。。
RE记录:差分约束题建边老是容易在Addedge处写倒掉。注意本题$n$的意味,不要混淆成点数。
总结:第三元参入不等式,二分答案检验。