【图论/差分约束】AcWing 393. 雇佣收银员

分析

这题就推一些柿子。

考虑时间点 \(i\in[0, 23]\) 安排了 \(a_i\) 人。

设时间点 \(i\) 最多来 \(lim_i\) 个员工,我们有约束:\(a_i \in [0, lim_i]\)

然后我们还需要满足每个时间段被 \(\geq R_i\) 人覆盖。

分个类:

  • \(i\in[7,23]\) 时,\(\sum_{k=i-7}^i a_k \geq R_i\)
  • \(i\in[0,6]\)\(\sum_{k=0}^i a_k + \sum_{k=17+i}^{23} a_k \geq R_i\)

上面的约束形式显然不是二元的,考虑使用前缀和(即 \(s_i = \sum_{k=0}^i a_k\))对式子进行变换,那么上面所有的约束等价于:

  • \(s_0 \geq 0\)
  • \(s_i \geq s_{i-1}\)
  • \(s_0 \leq lim_0\)
  • \(s_{i-1}\geq s_i - lim_i\)
  • \(i=7\)\(s_7 = R_7\)
  • \(i\in[8, 23]\),有 \(s_i - s_{i-8}\geq R_i\)
  • \(i\in [0,6]\),有 \(s_i - s_{i+16} \geq R_i - s_{23}\)

可以发现上面的式子有些不是二元形式,对于一元的式子,例如 \(s_0\geq 0\),我们可以定义一个零点 \(s_{24}\),那么满足 \(s_0 \geq s_{24} + 0\),这样便可以进行建边(也就是 \(24\) 号点向 \(0\) 号点连接权为 \(+0\) 的边)。

而对于三元的式子,其实就是最后一条式子,我们不妨固定(比如通过枚举)一元,这里考虑固定 \(s_{23}\)

但注意到 \(s_{23}\) 的意义就是我们决策的人数,显然,如果决策的人数为 \(k\) 时有解,那么 \(>k\) 时也必然有解,所以我们可以对 \(s_{23}\) 的值进行二分,这样就可以将图建出来了。

// Problem: 雇佣收银员
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/395/
// Memory Limit: 10 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;

#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()

#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;

inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=30;

const int n=24;
vector<pii> g[N];
int R[N];
int lim[N];

void add(int u, int v, int w){
    g[u].pb({v, w});
}

int d[N], cnt[N];
bool vis[N];

bool spfa(int x){
    // build graph
    rep(i,0,n) g[i].clear();

    add(n-1, n, -x), add(n, n-1, x);
    add(n, 0, 0);
    rep(i,1,n-1) add(i-1, i, 0);
    add(0, n, -lim[0]);
    rep(i,1,n-1) add(i, i-1, -lim[i]);
    add(n, 7, R[7]);
    rep(i,8,n-1) add(i-8, i, R[i]);
    rep(i,0,6) add(i+16, i, R[i]-x);


    memset(d, 0xcf, sizeof d);
    memset(vis, false, sizeof vis);
    memset(cnt, 0, sizeof cnt);

    d[n]=0;
    queue<int> q;
    q.push(n);
    vis[n]=true;

    while(q.size()){
        int u=q.front(); q.pop();
        vis[u]=false;

        for(auto &[go, w]: g[u]){
            if(d[go]<d[u]+w){
                d[go]=d[u]+w;
                if(!vis[go]){
                    if(++cnt[go]==n) return false;
                    vis[go]=true;
                    q.push(go);
                }
            }
        }
    }

    return true;
}

signed main(){
    int cs; cin>>cs;
    while(cs--){
        rep(i,0,n-1) read(R[i]);

        rep(i,0,n-1) lim[i]=0;
        int m; cin>>m;
        rep(i,1,m){
            int x; read(x);
            lim[x]++;
        }

        int l=0, r=m;
        while(l<r){
            int mid=l+r>>1;
            if(spfa(mid)) r=mid;
            else l=mid+1;
        }
        if(!spfa(l)) puts("No Solution");
        else cout<<d[n-1]<<endl;
    }   
    return 0;
}

posted @ 2022-07-17 16:39  HinanawiTenshi  阅读(35)  评论(0编辑  收藏  举报