hdu1839 二分+最短路

Delay Constrained Maximum Capacity Path

Time Limit: 10000/10000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 373    Accepted Submission(s): 149


Problem Description
Consider an undirected graph with N vertices, numbered from 1 to N, and M edges. The vertex numbered with 1 corresponds to a mine from where some precious minerals are extracted. The vertex numbered with N corresponds to a minerals processing factory. Each edge has an associated travel time (in time units) and capacity (in units of minerals). It has been decided that the minerals which are extracted from the mine will be delivered to the factory using a single path. This path should have the highest capacity possible, in order to be able to transport simultaneously as many units of minerals as possible. The capacity of a path is equal to the smallest capacity of any of its edges. However, the minerals are very sensitive and, once extracted from the mine, they will start decomposing after T time units, unless they reach the factory within this time interval. Therefore, the total travel time of the chosen path (the sum of the travel times of its edges) should be less or equal to T.
 

Input
The first line of input contains an integer number X, representing the number of test cases to follow. The first line of each test case contains 3 integer numbers, separated by blanks: N (2 <= N <= 10.000), M (1 <= M <= 50.000) and T (1 <= T <= 500.000). Each of the next M lines will contain four integer numbers each, separated by blanks: A, B, C and D, meaning that there is an edge between vertices A and B, having capacity C (1 <= C <= 2.000.000.000) and the travel time D (1 <= D <= 50.000). A and B are different integers between 1 and N. There will exist at most one edge between any two vertices.
 

Output
For each of the X test cases, in the order given in the input, print one line containing the highest capacity of a path from the mine to the factory, considering the travel time constraint. There will always exist at least one path between the mine and the factory obbeying the travel time constraint.
 

Sample Input
  
  
2 2 1 10 1 2 13 10 4 4 20 1 2 1000 15 2 4 999 6 1 3 100 15 3 4 99 4
 

Sample Output
  
  
13 99
 

Author
Mugurel Ionut Andreica
 

Source
 

Recommend
lcy
 
容量越大时间越长~以此二分~感觉不是很严谨额~~
先是未优化dijkstra10S还是超时~~~换spfa过~~~没有试优先队列优化的dijkstra
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define inf 99999999
using namespace std;
struct node
{
    int u,v,w,c;
};
node e[100005];
int first[10005];
int next[100005];
bool inq[10005];
int d[10005],n;
void add_edge(int u,int v,int c,int w,int i)
{
    e[i].u=u;
    e[i].v=v;
    e[i].w=w;
    e[i].c=c;
    next[i]=first[u];
    first[u]=i;
}
void spfa(int ff)
{
    memset(inq,0,sizeof(inq));
    int i;
    for(i=0;i<=n;i++)
        d[i]=inf;
    d[1]=0;
    queue<int> q;
    q.push(1);
    while(!q.empty())
    {
        int x=q.front(),y;
        q.pop();
        inq[x]=false;
        for(y=first[x];y!=-1;y=next[y])
        {
            int u=e[y].u;
            int v=e[y].v;
            if(d[v]>=d[x]+e[y].w&&e[y].c>=ff)
            {
                d[v]=d[x]+e[y].w;
                if(!inq[v])
                {
                    inq[v]=true;
                    q.push(v);
                }
            }
        }
    }
}
int main()
{
    int x,cas=0;
    scanf("%d",&x);
    for(cas=0;cas<x;cas++)
    {
        int m,t;
        scanf("%d%d%d",&n,&m,&t);
        int i;
        memset(first,-1,sizeof(first));
        memset(next,-1,sizeof(next));
        int Min=inf,Max=0;
        int k=0;
        for(i=0;i<m;i++)
        {
            int u,v,w,c;
            scanf("%d%d%d%d",&u,&v,&c,&w);
            if(c>Max)
                Max=c;
            if(c<Min)
                Min=c;
            add_edge(u,v,c,w,k++);
            add_edge(v,u,c,w,k++);
        }
        int ans;
        while(Min<=Max)
        {
            int l=(Min+Max)/2;
            spfa(l);
            if(d[n]<=t)
            {
                Min=l+1;
                ans=l;
            }
            else
                Max=l-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}


<think>好的,我现在需要解决用户关于POJ1275或HDU1529题目“Cashier Employment”的解法问题。用户希望得到解题思路和示例代码,所以我要先理解这个问题,然后整理出清晰的步骤。 首先,我应该回顾题目内容。根据引用[2]和[4],题目大意是:一个商店在不同时间段需要不同数量的出纳员,而每个应聘者一旦被雇佣,会连续工作8小时。任务是在满足各时间段人员需求的前提下,找到少需要雇佣的人数。 接下来,我需要确定解题方法。根据引用[3]和[4],这题使用了差分约束系统。差分约束通常用于处理变量间的差的不等式约束,通过构建图模型并利用短路径算法求解。 具体来说,引用[3]提到设定s[i]表示前i小时开始工作的人数总和。对于每个时间点i,当i>=8时,需要s[i] - s[i-8] >= r[i],其中r[i]是i时刻需要的人数。而当i<8时,约束变为s[i] + s[24] - s[i+16] >= r[i],这是因为工作时间跨越了24小时的周期。此外,相邻小时之间的开始人数差也有约束,例如0 <= s[i] - s[i-1] <= t[i],其中t[i]是i时刻可以开始工作的人数上限。 但这里s[24]是未知数,所以需要枚举可能的s[24]值,然后检查是否存在满足所有约束的解。这可以通过二分搜索来优化枚举过程,减少检查次数。 接下来,我需要考虑如何将这些约束转化为图的结构。每个不等式对应图的边,例如对于s[i] - s[j] >= k,可以转化为s[j] <= s[i] -k,这相当于从i到j的一条权值为-k的边。然后用Bellman-Ford或SPFA算法检测是否存在负环,以判断是否有解。 不过,用户可能需要具体的步骤和示例代码。因此,我需要整理出步骤: 1. 输入每个小时所需的人数r[0...23]和每个小时可应聘的人数t[0...23]。 2. 确定枚举s[24]的范围,可能从0到总应聘人数之和,或者使用二分法确定小可能值。 3. 对于每个候选的s[24]值,构建差分约束系统。 4. 添加约束条件: - 对于每个i,s[i] - s[i-1] >= 0; - s[i-1] - s[i] >= -t[i]; - 处理i>=8和i<8时的覆盖约束。 5. 使用SPFA算法判断是否存在可行解,若存在则记录小值。 6. 如果无法找到解,输出无解。 在编写示例代码时,需要注意图的构建和约束条件的处理。例如,如何处理跨天的约束,以及如何将s[24]作为变量处理。可能需要将时间点扩展到24小时,并处理模24的情况。 此外,需要注意差分约束系统中,通常需要添加一个超级源点,例如s[-1]=0,并连接到所有其他节点,以确保图的连通性。或者,根据题解中的方法,可能不需要显式的超级源点,而是通过将所有节点的初始距离设为0来处理。 检查引用中的示例,引用[3]提到枚举s[24],然后建立约束。例如,当枚举s[24]=ans时,需要添加约束s[24] - s[0] >= ans和s[0] - s[24] >= -ans,因为s[24]是一个确定的候选值,这时候将s[24]固定,并在约束中处理。 可能的代码结构如下: - 使用二分法确定小的ans(s[24])。 - 对于每个ans,构建约束: - 对于i >= 1,s[i] - s[i-1] >= 0; - s[i-1] - s[i] >= -t[i]; - 对于每个小时i: - 如果i >=8,则s[i] - s[i-8] >= r[i]; - 否则,s[i] + ans - s[i+16] >= r[i]; - s[24] - s[0] <= ans 且 s[0] - s[24] <= -ans(即s[24] = ans); - 然后检查这些约束是否可以被满足。 在实现时,可能需要将s[24]作为变量,但是在枚举时固定它的值为ans,并通过添加约束s[24] = ans,即两个不等式:s[24] >= ans 和 s[24] <= ans。 不过,根据引用[3],当枚举ans时,s[24]就是ans,所以在约束中需要将s[24]视为已知的ans。例如,当处理i<8时,约束变为s[i] + ans - s[i+16] >= r[i],即s[i] - s[i+16] >= r[i] - ans。这可能需要在代码中动态调整约束条件。 接下来,编写代码的大致步骤: 1. 预处理输入数据,读取r数组和t数组。 2. 使用二分法确定小可能的ans值,左边界为0,右边界为可能的大值,比如sum(t)或者某个较大的数。 3. 对于每个mid值(候选ans),构建差分约束图,添加所有相关边。 4. 运行SPFA算法检查是否存在负环,若存在则说明无解,否则有解。 5. 根据是否存在解调整二分边界。 需要注意的是,差分约束系统中,每个变量s[i]对应图中的节点,边表示约束条件。例如,对于约束a - b <= c,可以添加一条从b到a的边,权值为c。或者根据具体不等式调整边的方向。 例如,对于s[i] - s[i-8] >= r[i](i>=8),可以转化为s[i-8] <= s[i] - r[i],即添加边从i到 i-8,权值为 -r[i]。 对于i<8的情况,约束是s[i] + ans - s[i+16] >= r[i],即s[i] - s[i+16] >= r[i] - ans。转化为s[i+16] <= s[i] + (ans - r[i]),所以添加从i+16到i的边,权为 ans - r[i]。 同时,对于每个i,s[i] >= s[i-1],即s[i-1] <= s[i],对应边从i到 i-1,权0;而s[i] - s[i-1] <= t[i],即s[i] <= s[i-1] + t[i],对应边从i-1到 i,权t[i]。 此外,对于s[24] = ans的约束,即s[24] >= ans 和 s[24] <= ans。s[24] >= ans 转化为 s[0] <= s[24] - ans,添加边从24到0,权为 -ans;s[24] <= ans 转化为 s[24] <= s[0] + ans,添加边从0到24,权为 ans。 或者,可能还有其他方式处理s[24]的固定值。这部分可能需要更仔细的分析。 在实现时,可能将每个约束转化为边,例如: 对于约束s[j] - s[i] >= k,转化为s[i] <= s[j] -k,对应的边是i -> j,权为 -k。 这样,构建图后,使用SPFA算法计算短路,如果存在负环,则无解。 但需要注意节点编号的问题。例如,时间是从0到23,所以节点可能是0到23,而如何处理s[24]?可能s[24]实际上是ans,所以需要特殊处理。 或者,可能s数组是s[0...24],其中s[24]就是ans,因此在枚举ans时,需要将s[24]固定为ans。此时,在约束中s[24]是一个变量,但需要满足s[24] = ans,即添加两个约束:s[24] - s[0] >= ans 和 s[0] - s[24] >= -ans。这可能对应到边0->24的权为ans,和24->0的权为-ans。 这可能需要更详细的推导。例如,假设s[24] = ans,那么: s[24] - s[0] >= ans → ans - s[0] >= ans → s[0] <= 0 同时,s[0] - s[24] >= -ans → s[0] - ans >= -ans → s[0] >=0 所以s[0]必须等于0?这可能有问题,可能需要重新考虑。 或者,可能原来的约束是,在枚举ans时,将s[24]设为ans,并添加约束s[24] = ans,这可以通过添加两个不等式:s[24] >= ans 和 s[24] <= ans。但是如何将这些转化为边? 例如,s[24] <= ans → s[24] - s[0] <= ans - s[0],这可能不太容易处理。或者,将s[24]视为变量,并添加: s[24] >= ans → s[0] - s[24] <= -ans s[24] <= ans → s[24] - s[0] <= ans - s[0] 这可能比较复杂。所以,可能更简单的方式是,在枚举ans时,将s[24]固定为ans,并添加相应的约束。 例如,当处理i<8的约束时: s[i] + ans - s[i+16] >= r[i] → s[i] - s[i+16] >= r[i] - ans → s[i+16] <= s[i] + (ans - r[i]) 这相当于添加一条从i到 i+16的边,权为 (ans - r[i])。或者,根据约束转换规则,对于s[j] - s[i] >= k → s[i] <= s[j] -k → 边i→j,权为 -k。这里约束是s[i] - s[j] >= (r[i] - ans),即 s[j] <= s[i] - (r[i] - ans),所以边j→i,权为 -(r[i] - ans) = ans - r[i]. 所以,正确的边应该是从j到i,权为 ans - r[i],其中j = i+16。 比如,当i=0时,i+16=16,所以约束是s[0] - s[16] >= r[0] - ans → s[16] <= s[0] - (r[0] - ans) → 边16→0,权为 -(r[0] - ans) = ans - r[0]。 这样,在构建图时,对于每个i<8的情况,需要添加从i+16到i的边,权为 ans - r[i]。 同时,对于i>=8的情况,约束是s[i] - s[i-8] >= r[i],即边i-8→i,权为 -r[i]。 此外,对于每个i,s[i] - s[i-1] >=0 → 边i-1→i,权0; s[i] - s[i-1] <= t[i] → s[i] <= s[i-1] + t[i] → 边i→i-1,权 t[i]. 还有,s[24]必须等于ans。这可以通过添加两个约束: s[24] >= ans → s[0] <= s[24] - ans → 边24→0,权为 -ans; s[24] <= ans → s[24] <= s[0] + ans → 边0→24,权为 ans. 或者,这可能与之前的处理方式不同。可能需要重新考虑如何将s[24]固定为ans。 假设s[24]是变量,而我们希望s[24] = ans。那么,可以添加约束s[24] >= ans 和 s[24] <= ans。这样,s[24]必须等于ans。这两个约束可以转化为: s[24] - s[0] >= ans → 边0→24,权为 -ans; s[0] - s[24] >= -ans → 边24→0,权为 -ans; 或者这可能不是正确的转换方式。或者,可能应该建立超级源点,例如创建节点-1,并连接所有节点到它,以确保图的连通性。但根据差分约束的一般做法,通常添加一个超级源点s,并添加边s到所有其他节点,权为0,这样可以通过Bellman-Ford算法检测是否存在负环。 不过,在这种情况下,可能不需要超级源点,因为每个s[i]的约束已经足够,但需要确保图是连通的。例如,通过边i-1→i的权0,边i→i-1的权t[i],以及其它约束,可能图已经是连通的。 现在,我需要将这些分析转化为代码结构。假设节点是0到24(其中s[24]对应ans),那么在枚举ans时,如何处理? 可能的代码步骤如下: 对于每个候选的ans,构建包含节点0到24的图,然后: 1. 添加边i-1→i,权0(对应s[i] >= s[i-1])。 2. 添加边i→i-1,权t[i](对应s[i-1] >= s[i] - t[i])。 3. 对于每个i,0<=i<24: a. 如果i >=8,添加边i-8→i,权为 -r[i]。 b. 如果i <8,添加边i+16→i,权为 ans - r[i]。 4. 添加边0→24,权为 ans(对应s[24] <= s[0] + ans)。 5. 添加边24→0,权为 -ans(对应s[24] >= s[0] + ans → s[0] <= s[24] - ans)。 然后,运行SPFA算法检查是否存在负环。如果存在,说明无解;否则,有解。 或者,可能步骤4和5的处理方式需要调整。例如,要使得s[24] = ans,可能需要添加: s[24] - s[0] >= ans → s[0] <= s[24] - ans → 边24→0,权为 -ans. s[24] - s[0] <= ans → s[24] <= s[0] + ans → 边0→24,权为 ans. 这两条边将确保s[24] - s[0] >= ans和s[24] - s[0] <= ans,因此s[24] - s[0] = ans。但是这可能并不直接导致s[24] = ans,除非s[0]为0。这可能需要更多的分析。 或者,正确的处理方式是将s[24]固定为ans。例如,在枚举ans时,s[24]的值是ans,所以将其视为一个已知值,并将约束表达式中的s[24]替换为ans。例如,在i<8的情况下,约束变为s[i] + ans - s[i+16] >= r[i],即s[i] - s[i+16] >= r[i] - ans → s[i+16] <= s[i] + (ans - r[i]) → 添加边i+16→i,权为 (ans - r[i]). 在这种情况下,s[24]是已知的ans,所以在构建图时,不需要将s[24]作为变量,而是将其视为常量。这可能需要调整节点的数量,例如只处理0到23的节点,而s[24]作为ans,但这样处理可能比较复杂。 这可能就是为什么引用[3]中提到需要枚举s[24]的值,并将其作为变量来处理。因此,可能正确的做法是,在差分约束系统中,s[24]是一个变量,但我们需要强制它等于当前的候选值ans。这可以通过添加两个约束: s[24] >= ans → s[0] <= s[24] - ans → 边24→0,权为 -ans. s[24] <= ans → s[24] <= s[0] + ans → 边0→24,权为 ans. 这样,当这两个约束同时存在时,s[24]必须等于ans。例如,假设s[0] =0,那么: s[24] >= ans → s[24] >= ans. s[24] <= s[0] + ans → s[24] <= ans. 所以s[24]必须等于ans。而s[0]的值可能根据其他约束被确定为0,或者可能不为0,这需要更多的分析。 这似乎是一个可能的解决方案。因此,在代码中,当枚举ans时,添加这两个约束,以确保s[24] = ans。 现在,将这些约束转化为边的添加: 对于s[24] >= ans → s[24] - s[0] >= ans → 边0→24,权为 -ans. 对于s[24] <= ans → s[24] - s[0] <= ans → 边24→0,权为 ans. 或者,可能转换方式有误。比如,对于s[24] >= ans,可以改写为s[24] - s[0] >= ans → s[0] <= s[24] - ans → 边24→0,权为 -ans. 对于s[24] <= ans → s[24] <= ans → 因为s[24]是一个变量,这里需要另一个参考点。假设s[0]是0,那么这可能无法直接应用。或者,可能应该添加约束s[24] <= ans,这可以转化为s[24] - s[0] <= ans - s[0] → 但这可能无法直接转换为边。 可能更好的方法是将s[24]直接与ans比较。例如,添加约束s[24] >= ans和s[24] <= ans,这样s[24]必须等于ans。但如何在差分约束系统中处理这两个约束? 在差分约束中,约束必须是形如x_j - x_i <= c。因此: s[24] >= ans → s[24] - s[0] >= ans - s[0] 这似乎无法直接转换。另一种方法是引入一个固定节点,例如超级源点,比如s[-1] =0,然后添加s[24] >= ans → s[24] - s[-1] >= ans → 边-1→24,权为 ans. 同时,s[24] <= ans → s[24] - s[-1] <= ans → 边24→-1,权为 -ans. 这可能更合理,因为这样s[24]必须等于ans。因此,在代码中,添加这两个边: s[24] - s[-1] >= ans → s[-1] → s[24],权为 ans. s[24] - s[-1] <= ans → s[24] → s[-1],权为 -ans. 这样,结合s[-1] =0,就能保证s[24] = ans. 但这需要引入超级源点s[-1],并设置其距离为0。这可能使问题更容易处理。 因此,整个差分约束系统将包括节点-1(超级源点),0到24。然后: - 超级源点s[-1] =0,所有其他节点的初始距离为0。 - 添加边从-1到每个节点i,权为0,以确保图的连通性。 - 添加边从s[-1]到24,权为ans(对应s[24] >= ans)。 - 添加边从24到s[-1],权为 -ans(对应s[24] <= ans)。 这样,s[24]必须等于ans,因为s[24] >= ans 和 s[24] <= ans。 这可能是一个更合理的处理方式。因此,在代码中,节点包括0到24,以及超级源点,比如编号为25或单独处理。 不过,这可能增加代码的复杂性。例如,在SPFA实现中,需要处理节点数较多的情况。或者,可能不需要显式的超级源点,而是通过其他方式处理。 现在,我需要总结这些分析,并整理出步骤清晰的解题思路,然后编写示例代码。 可能的解题思路步骤: 1. 输入每个小时所需的人数r[0..23]和每个小时可以开始工作的人数t[0..23]。 2. 使用二分法枚举可能的答案ans(少雇佣人数),范围从0到sum(t)或者某个大值。 3. 对于每个候选的ans,构建差分约束系统: a. 添加约束:s[i] - s[i-1] >= 0 → 边i-1→i,权0。 b. 添加约束:s[i-1] - s[i] >= -t[i] → 边i→i-1,权t[i]。 c. 对于每个i,处理覆盖约束: i. 如果i >=8 → s[i] - s[i-8] >= r[i] → 边i-8→i,权-r[i]. ii. 如果i <8 → s[i] + ans - s[i+16] >= r[i] → s[i] - s[i+16] >= r[i] - ans → 边i+16→i,权ans - r[i]. d. 添加约束s[24] = ans: i. s[24] >= ans → 边超级源点→24,权ans。 ii. s[24] <= ans → 边24→超级源点,权-ans。 4. 使用SPFA算法检查是否存在负环,如果不存在,说明当前ans可行,尝试更小的值;否则,增大ans。 5. 终找到小的可行ans,若无解则输出对应信息。 在代码中,可能需要将超级源点处理为节点25,并添加相应的边。或者,直接使用现有的节点结构。 例如,在代码中,节点0到24表示s[0]到s[24],而超级源点为节点25,并添加边: - 25到i,权0(对于所有i)。 - 25到24,权ans → 对应s[24] >= ans. - 24到25,权-ans → 对应s[24] <= ans. 然后,运行SPFA时,以25为源点,检查是否存在负环。 不过,这可能在实际代码中比较复杂。另一种方法是,在每次枚举ans时,添加以下约束: s[24] - s[0] >= ans → 边0→24,权为 -ans. s[0] - s[24] >= -ans → 边24→0,权为 ans. 这相当于将s[24] = s[0] + ans,这可能无法确保s[24] = ans,除非s[0] =0。但这可能不正确。 因此,可能必须引入超级源点才能正确约束s[24] = ans。 综上,正确的做法可能包括: - 将超级源点作为节点,并添加相应的边,以确保s[24] = ans。 不过,由于时间和精力有限,我需要参考已有的题解,比如引用[3]中的思路,其中提到枚举s[24],并将其作为变量,然后建立相应的约束。例如,在约束中,s[24]被作为变量,但我们需要其等于ans,因此添加约束s[24] = ans。这可能通过添加两个约束: s[24] >= ans → s[0] <= s[24] - ans → 边24→0,权为 -ans. s[24] <= ans → s[24] <= ans → 这可能需要另一个参考点,比如超级源点s[25] =0,则添加边25→24,权ans → s[24] <= ans + s[25] → s[24] <= ans. 同时,添加边24→25,权 -ans → s[25] <= s[24] - ans → 因为 s[25] =0 → s[24] >= ans. 这样,两个边确保s[24] = ans. 综上,代码可能需要处理这些边,然后运行SPFA检查是否有解。 现在,根据这些分析,编写示例代码的大纲: 读取输入r和t数组。 初始化low=0,high=可能的max_ans. 答案初始化为-1. 二分循环: mid = (low + high) //2. 构建约束: 清除图的边。 添加边i-1 →i,权0. 添加边i→i-1,权t[i]. for i in 0..23: if i >=8: 添加边i-8 →i,权 -r[i]. else: 添加边i+16 →i,权 mid - r[i]. 添加边24→0,权 -mid. 添加边0→24,权 mid. 添加超级源点25到所有节点的边,权0. 运行SPFA检查是否有负环。 if 有解: 记录ans=mid. high=mid-1. else: low=mid+1. 后输出ans. 不过,实际代码中可能需要更详细的处理,例如节点的数量,边的存储方式等。 现在,编写示例代码时需要注意: - 使用SPFA算法,每个节点入队次数超过总节点数则存在负环。 - 节点编号可能需要调整为0到24,或者包含超级源点。 例如,节点数为25(0-24),超级源点25。 或者,将超级源点作为节点0,其他节点1-25对应s[0]到s[24]。这可能因具体实现而异。 或者,另一种方式是,在每次枚举ans时,将s[24]视为ans,并直接处理约束条件中的s[24],例如,在i<8的约束中,将s[24]替换为ans,从而不需要将s[24]作为变量,此时节点仅需0-23。这可能更简单。 比如,假设s[24] = ans,那么: 对于i<8的情况,约束是s[i] + ans - s[i+16] >= r[i] → s[i] - s[i+16] >= r[i] - ans → 边i+16→i,权为 ans - r[i]. 此时,s[24]不在变量范围内,而是作为已知的ans。这样,节点只需要0-23,处理s[0]到s[23]。 然后,还需要添加约束,确保s[24] = ans,但这可能因为s[24]不是变量而无法处理。因此,这可能需要重新设计约束。 这可能是一个可能的错误点。因此,正确的做法可能需要将s[24]作为变量,并在枚举ans时,添加约束s[24] = ans。这可能需要将节点扩展到24,表示s[0]到s[24]。 然后,在差分约束中,添加: s[24] >= ans → 边 from 24 to 超级源点,权 ans? 不太清楚。 可能这需要更深入的分析。 考虑到时间限制,我需要参考已有的正确代码。比如,引用[3]中的思路: “在这里会发现 s[24] 就是我们要求的答案,但就这样的话还是求不出,所以枚举s[24]” 这意味着,在差分约束系统中,s[24]是一个变量,而我们需要枚举它的可能值,并在每次枚举时,添加约束s[24] = ans,然后检查其他约束是否满足。 如何将s[24] = ans 添加到差分约束系统中?这需要添加: s[24] >= ans → s[24] - s[0] >= ans - s[0] ? 或者,可能直接添加两个约束: s[24] >= ans 和 s[24] <= ans. 在差分约束中,这可以转化为: s[24] - s[0] >= ans → 边0→24,权为 -ans. s[24] - s[0] <= ans → 边24→0,权为 ans. 但这可能无法正确约束s[24] = ans,除非s[0] =0,这可能不一定成立。 因此,可能正确的做法是,引入一个超级源点,例如节点0,并强制s[0] =0。然后,添加约束s[24] = ans → s[24] - s[0] = ans → s[24] - s[0] >= ans 和 s[24] - s[0] <= ans. 这可以通过添加两条边: 从0到24,权为 ans → s[24] <= s[0] + ans. 从24到0,权为 -ans → s[0] <= s[24] - ans. 结合s[0] =0,这将强制s[24] = ans. 这样,在代码中: 强制s[0] =0,并添加边0→24,权ans,和边24→0,权-ans. 这可能有效。 因此,在构建约束时,首先强制s[0] =0,这可能通过添加边从超级源点到0,权0,并设置超级源点的距离为0。 综上,正确的代码可能需要: 1. 对于每个枚举的ans,添加以下约束: a. s[0] =0 → 这可以通过添加超级源点,例如节点-1,连接到0,权0,然后所有节点到超级源点的边权为0。 b. s[24] = ans → 添加边0→24,权ans(s[24] <= s[0] + ans)和边24→0,权-ans(s[0] <= s[24] - ans). 此外,其他约束如前所述。 现在,编写伪代码: 输入r和t数组。 low = 0 high = sum(t) 或者可能的大值. ans = -1 while low <= high: mid = (low + high) //2 构建图: 清空边列表. 添加超级源点到所有节点的边,权0. 添加边0→24,权mid. 添加边24→0,权 -mid. 对于i from 1 to24: 添加边i-1 →i,权0. 添加边i→i-1,权t[i]. 对于i from0 to23: if i >=8: 添加边i-8 →i,权 -r[i]. else: 添加边i+16 →i,权 mid - r[i]. 运行SPFA,检查是否有负环. if 没有负环: ans = mid high = mid -1 else: low = mid +1 输出ans. 但需要注意,超级源点的处理可能不同。例如,SPFA可能需要从超级源点出发,判断是否存在负环。或者,因为所有节点的初始距离为0,可能不需要显式处理超级源点。 或者,可能不需要超级源点,而是将所有节点的初始距离设为0,并在SPFA中检查所有节点是否可能被松弛。 例如,在SPFA算法中,初始时将所有节点的距离设为0,然后对所有节点进行入队,如果发现某个节点的距离可以被更新,则说明存在负环。 这可能更简单,不需要超级源点。 综上,示例代码的编写可能如下(以Python为例): 使用邻接表存储图,每个节点i的邻接表存储目标节点和权值。 对于每个测试案例: 读取r[0..23] 读取t[0..23] 定义函数 check(ans): 构建图. 添加边: # 超级源点导致所有节点可达?或者不需要 # 这里可能不需要显式的超级源点,而是直接处理约束 edges = [] # s[0] =0 的约束?或者不需要? # 添加边0→24,权ans,和边24→0,权-ans. edges.append( (0, 24, ans) ) edges.append( (24, 0, -ans) ) # 添加其他约束 for i in range(1,25): # s[i] >= s[i-1] edges.append( (i-1, i, 0) ) # s[i-1] >= s[i] - t[i] edges.append( (i, i-1, t[i]) ) for i in range(24): if i >=8: # s[i] - s[i-8] >= r[i] j = i-8 edges.append( (j, i, -r[i]) ) else: # s[i] + ans - s[i+16] >= r[i] # s[i] - s[i+16] >= r[i] - ans j = i +16 edges.append( (j, i, ans - r[i]) ) # 运行SPFA判断是否存在负环 # 节点是0-24 n = 25 dist = [0]*n in_queue = [False]*n cnt = [0]*n from collections import deque q = deque() for i in range(n): q.append(i) in_queue[i] = True has_cycle = False while q: u = q.popleft() in_queue[u] = False for (u, v, w) in edges: # 这里需要遍历所有以u为起点的边 # 假设edges存储的是所有边,需要筛选u为起点的边 # 可能需要预处理邻接表 # 修改此处,使用邻接表结构 # 例如,邻接表graph[u]包含所有 (v, w) # 所以,这里应提前构建邻接表: # 预处理邻接表: graph = [[] for _ in range(n)] for (a, b, c) in edges: graph[a].append( (b, c) ) # 现在,遍历u的所有邻接边: for (v, w) in graph[u]: if dist[v] > dist[u] + w: dist[v] = dist[u] + w if not in_queue[v]: q.append(v) in_queue[v] = True cnt[v] +=1 if cnt[v] >=n: has_cycle = True return False return not has_cycle 但是,这里存在几个问题: 1. 边的存储方式:在函数check中,每次都要重新构建邻接表,这可能效率较低,但对于题目而言可能可以接受。 2. SPFA的实现是否正确,特别是如何处理所有可能的边。 另外,需要注意的是,在差分约束系统中,每个约束对应一条边,因此必须正确添加所有边。 此外,边的方向必须正确。例如,对于约束s[j] <= s[i] + w,添加边i→j,权w。 现在,测试这个函数是否正确。 例如,当ans是可行解时,check(ans)应返回True。 综上,这可能是一个可能的实现方式。 后,根据这个思路,编写代码。然后,用户可能需要这个示例代码,以及解题思路的总结。</think>### POJ1275/HDU1529 Cashier Employment 解题思路 该问题属于**差分约束系统**的应用,核心是通过不等式约束建立图模型,并利用**SPFA算法**检测负环来判断可行性。以下是具体步骤: 1. **变量定义** 设$s[i]$表示前$i$小时(0-based)开始工作的员工总数。目标是小化$s[24]$(即总雇佣人数)。 2. **约束条件分析** - **时段覆盖约束**: - 当$i \geq 8$时,$s[i] - s[i-8] \geq r[i]$,确保$i$时刻有足够员工工作[^3]。 - 当$i < 8$时,$s[i] + s[24] - s[i+16] \geq r[i]$(因员工连续工作8小时,跨天覆盖)[^3]。 - **相邻时段约束**: - $0 \leq s[i] - s[i-1] \leq t[i]$,即每小时新增员工数不超过应聘者数量[^3]。 - **总人数约束**: - 终需满足$s[24] = ans$(枚举可能的$ans$值)。 3. **差分约束建图** 将不等式转化为边: - $s[i] - s[i-1] \geq 0$ → 边$i-1 \rightarrow i$,权0。 - $s[i-1] - s[i] \geq -t[i]$ → 边$i \rightarrow i-1$,权$t[i]$。 - 时段覆盖约束根据$i$是否跨天分别建边。 - 通过枚举$s[24]=ans$并添加约束$s[24] \geq ans$和$s[24] \leq ans$,确保其值固定。 4. **二分枚举与SPFA检测** 使用**二分法**确定小$ans$,对每个候选值构建差分约束图,通过SPFA检测负环。若无负环,则当前$ans$可行。 ### 示例代码 ```python from collections import deque def solve(): r = list(map(int, input().split())) # 各小时所需人数 t = list(map(int, input().split())) # 各小时应聘人数 def check(ans): n = 25 # 节点0~24 edges = [] # s[24] = ans 的约束 edges.append((0, 24, ans)) # s[24] <= s[0] + ans edges.append((24, 0, -ans)) # s[0] <= s[24] - ans # 相邻小时约束 for i in range(1, 25): edges.append((i-1, i, 0)) # s[i] >= s[i-1] edges.append((i, i-1, t[i-1])) # s[i-1] >= s[i] - t[i-1] (注意t的索引) # 时段覆盖约束 for i in range(24): if i >= 8: # s[i] - s[i-8] >= r[i] edges.append((i-8, i, -r[i])) else: # s[i] + ans - s[i+16] >= r[i] → s[i] - s[i+16] >= r[i] - ans edges.append((i+16, i, ans - r[i])) # SPFA检测负环 graph = [[] for _ in range(n)] for a, b, c in edges: graph[a].append((b, c)) dist = [0] * n in_queue = [False] * n cnt = [0] * n q = deque() for i in range(n): q.append(i) in_queue[i] = True has_cycle = False while q and not has_cycle: u = q.popleft() in_queue[u] = False for v, w in graph[u]: if dist[v] > dist[u] + w: dist[v] = dist[u] + w if not in_queue[v]: q.append(v) in_queue[v] = True cnt[v] += 1 if cnt[v] >= n: has_cycle = True break return not has_cycle # 二分查找小ans left, right = 0, sum(t) ans = -1 while left <= right: mid = (left + right) // 2 if check(mid): ans = mid right = mid - 1 else: left = mid + 1 print(ans if ans != -1 else "No Solution") solve() ``` ### 注意事项 - 输入需按小时顺序提供`r[0]~r[23]`和`t[0]~t[23]`。 - 若无解,输出`No Solution`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值