D119 最短路→差分约束 SPFA 算法 P10941 [ICPC 2000 Tehran R] Cashier Employment 雇佣收银员
D119 最短路→差分约束 SPFA 算法 P10941 [ICPC 2000 Tehran R] Cashier Employment 雇佣收银员_哔哩哔哩_bilibili
P10941 [ICPC 2000 Tehran R] Cashier Employment - 洛谷
求满足约束条件的最小解。
思路
$r[i]$ 表示 $i$ 时刻需要的人数,$num[i]$ 表示 $i$ 时刻来的人数,$x[i]$ 表示 $i$ 时刻雇佣的人数
1. $i$ 时刻雇佣的人数应小于等于来的人数:$0\le x[i]\le num[i]$
2. 从 $i-7$ 到 $i$ 时刻雇佣的人都能在 $i$ 时刻工作,即 $x[i-7]+x[i-6]+...+x[i]\ge r[i]$
使用前缀和 $s[i]=x[1]+x[2]+...+x[i]$
1. $i$ 时刻雇佣的人数应小于等于来的人数:$0\le s[i]-s[i-1] \le num[i]$
2. 当 $8\le i\le 24$ 时,$s[i]-s[i-8]\ge r[i]$
当 $1\le i\le 7$ 时,$s[i]+s[24]-s[i+16]\ge r[i]$,如 $i=2, x[19]+...+x[24]+x[1]+x[2]\ge r[2]$
因为求最小解,约束条件均转化为 $\ge$
1. $s[i]-s[i-1]\ge 0,1\le i\le 24$
2. $s[i-1]-s[i]\ge -num[i],1\le i\le 24$
3. $s[i]-s[i-8]\ge r[i],8\le i\le 24$
4. $s[i]-s[i+16]\ge r[i]-s[24],1\le i\le 7$
约束关系 4 中有 $s[24]$ 这个变量,其实 $s[24]=x[1]+x[2]+...+x[24]$ 就是待求的答案
我们可以二分答案,二分一个 $mid$,$s[i]-s[i+16]\ge r[i]-mid,1\le i\le 7$
$s[24]=mid$ 转化成 $mid\le s[24]\le mid$
即 $s[24]-s[0]\ge mid$ 且 $s[0]-s[24]\ge -mid,s[0]=0$
我们把变量 $s[i]$ 看做点,点的编号即下标 $i$,连边建图
从第一个 add 可知,0 点就是超级源点(0 边权到达各点),所以从 0 点出发跑最长路

当 $mid\ge ans$ 时,一定满足要求,无正环;当 $mid<ans$ 时,一定不满足要求,有正环

点数 $N\le 25$,边数 $M\le 25∗3=75$,所以时间复杂度 $O(T*1000*N*M)$
相关板子:
D116【模板】最短路→差分约束 SPFA 算法 P5960 差分约束 - 董晓 - 博客园
// 差分约束 SPFA 算法 O(NM*logN) #include<bits/stdc++.h> using namespace std; const int N=25,M=100; int h[N],to[M],ww[M],ne[M],idx; void add(int a,int b,int c){ to[++idx]=b,ww[idx]=c,ne[idx]=h[a],h[a]=idx; } int n; int r[N],num[N]; int d[N],cnt[N],vis[N]; void build(int mid){ idx=0; memset(h,0,sizeof h); for(int i=1; i<=24; i++){ add(i-1,i,0); add(i,i-1,-num[i]); } for(int i=8; i<=24; i++) add(i-8,i,r[i]); for(int i=1; i<=7; i++) add(i+16,i,r[i]-mid); add(0,24,mid); add(24,0,-mid); } bool spfa(int mid){ build(mid); //建图 memset(d,-0x3f,sizeof d); d[0]=0; memset(cnt,0,sizeof cnt); memset(vis,0,sizeof vis); queue<int> q; q.push(0);vis[0]=true; //0就是超级源点 while(!q.empty()){ int u=q.front(); q.pop();vis[u]=false; for(int i=h[u]; i; i=ne[i]){ int v=to[i]; if(d[v]<d[u]+ww[i]){ d[v]=d[u]+ww[i]; //最长路 cnt[v]=cnt[u]+1; //走过的边数 if(cnt[v]>=25) return 1; //有正环 if(!vis[v]) q.push(v),vis[v]=true; } } } return 0; //无正环 } int main(){ int T; cin>>T; while(T--){ for(int i=1; i<=24; i++)cin>>r[i]; //每个时刻的需要人数 cin>>n; memset(num,0,sizeof num); for(int i=1,t; i<=n; i++){ cin>>t; num[t+1]++; //每个时刻的应聘人数 } int l=-1,r=n+1; while(l+1<r){ int mid=l+r>>1; if(!spfa(mid)) r=mid; //如果无正环,说明人多 else l=mid; } if(r==n+1) cout<<"No Solution\n"; else cout<<r<<"\n"; } }
浙公网安备 33010602011771号