题解ZOJ1420 Cashier Employment

Posted twilight-sx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解ZOJ1420 Cashier Employment相关的知识,希望对你有一定的参考价值。

  论文——冯威《浅析差分约束系统》。

  论文讲得很详细,就不解释了。主要想记录一下对于差分约束的理解(感觉以前的学习真的是在囫囵吞枣啊……)

  差分约束系统,同于解决线性的不等关系是否存在合法解 & 求得最大 / 最小解。当其中牵涉到的式子形如 (A[i] - A[i - 1] >= (<=) x) 时,就可以运用差分约束求解了。处理的方法是由于这个式子为三角形不等式,即spfa中的松弛操作,所以我们看做一个图论的问题,跑最长路 最短路即可。连边的方式自己画图感知就好了。

  当需要求解最大 / 最小值时:最大值运用最短路,最小值运用最长路。以求最大值为例:一个数能够取得的最大值即在满足了最小的约束情况下可以取得的最大值,而道路边权即为约束边,所以为最短路。

  通用解法:1.罗列出不等关系(注意要找全了)。2.移项,未知数一边,常数一边。3.考虑运用最长路 / 最短路求解。这题主要在于构建出方程与函数来描述不等关系的特征,当常数中有未知项时,可以考虑枚举求解(其实这个思想很重要,数据范围小的时候一定考虑枚举暴力呀)。当然这题由于满足单调性,所以二分一下~

#include <bits/stdc++.h>
using namespace std;
#define maxn 300000
int cnp = 1, head[maxn], num[maxn];
int R[maxn], dis[maxn], in[maxn];
bool vis[maxn];

struct edge
{
    int to, last, co;
}E[maxn];

int read()
{
    int x = 0, k = 1;
    char c;
    c = getchar();
    while(c < 0 || c > 9) { if(c == -) k = -1; c = getchar(); }
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar();
    return x * k;
}

void add(int u, int v, int w)
{
    E[cnp].to = v, E[cnp].co = w;
    E[cnp].last = head[u], head[u] = cnp ++;
}

bool spfa()
{
    queue <int> q;
    for(int i = 1; i <= 30; i ++)
        dis[i] = -99999999;
    memset(vis, 0, sizeof(vis));
    memset(in, 0, sizeof(in));
    dis[0] = 0, q.push(0);
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        vis[u] = 0;
        for(int i = head[u]; i; i = E[i].last)
        {
            int v = E[i].to;
            if(dis[v] < dis[u] + E[i].co)
            {
                dis[v] = dis[u] + E[i].co;
                if(!vis[v])
                {
                    if(++ in[v] == 26) return 1;
                    vis[v] = 1; q.push(v);
                } 
            }
        }
    }
    return 0;
}

void Build(int mid)
{
    memset(head, 0, sizeof(head)); cnp = 1;
    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 < 8; i ++)
        add(i + 16, i, R[i] - mid);
    add(0, 24, mid); 
}

int main()
{
    int T = read();
    while(T --)
    {
        for(int i = 1; i <= 24; i ++) R[i] = read();
        int n = read();
        memset(num, 0, sizeof(num));
        for(int i = 1; i <= n; i ++)
        {
            int x = read();
            num[x + 1] ++;
        }
        Build(n);
        if(spfa())
        { 
            printf("No Solution
");
            continue;
        }
        int l = 0, r = n, ans;
        while(l <= r)
        {
            int mid = (l + r) >> 1;
            Build(mid);
            if(spfa()) l = mid + 1;
            else ans = mid, r = mid - 1;
        }
        printf("%d
", ans);
    }
    return 0;
}

 

以上是关于题解ZOJ1420 Cashier Employment的主要内容,如果未能解决你的问题,请参考以下文章

ZOJ--题解目录

ZOJ - 2477 Magic Cube 题解

ZOJ2314:Reactor Cooling——题解

ZOJ1081:Points Within——题解

ZOJ1937:Addition Chains——题解

ZOJ3496:Assignment——题解