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

分析

这题就推一些柿子。

考虑时间点 i[0,23] 安排了 ai 人。

设时间点 i 最多来 limi 个员工,我们有约束:ai[0,limi]

然后我们还需要满足每个时间段被 Ri 人覆盖。

分个类:

  • i[7,23] 时,k=i7iakRi
  • i[0,6]k=0iak+k=17+i23akRi

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

  • s00
  • sisi1
  • s0lim0
  • si1silimi
  • i=7s7=R7
  • i[8,23],有 sisi8Ri
  • i[0,6],有 sisi+16Ris23

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

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

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

// 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 @   HinanawiTenshi  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示