zoukankan      html  css  js  c++  java
  • hdu 1529 Cashier Employment 差分约束系统

    hdu 1529 Cashier Employment 差分约束系统
    //hdu 1529 Cashier Employment
    //差分约束系统
    //还有国家集训队的黄源河的论文中的这题的后面两个不等式写错了
    
    //不懂差分约束可以到这看看
    //http://imlazy.ycool.com/post.1702305.html;
    //一开始看我完全没想到可以用差分约束
    //这题也主要是看了别人代码才过了的,还用了蹩脚的英文注释
    //不过真感觉这题挺神奇的
    
    //题意:
    //第一行t,表示有几组测试数据;第二行24个数分别表示各个小时
    //至少需要的出纳员数;接下去一行一个数表m示有几个应聘者
    //接下去有m行,每行1个数表示该应聘者的开始工作时间
    //每个出纳员若开始工作时间为st 则他一定连续工作8小时
    //即工作到 st+8 小时
    
    //思路:
    //根据差分约束,我们可以先找出一些看的到的不等式
    //s[i] 表示在从第0小时到第i个小时总的需要多少出纳员,则我们
    //要求的就是s[23]了;w[i]表示应聘第i个小时开始工作的出纳员数
    //所以我们要在输入的时候累加每个小时的应聘的出纳员数;
    //r[i]表示第i个小时至少需要多少出纳员
    //1、0 <= s[i] - s[i-1] <= w[i] (0 <= i <= 23)
    //2、s[i] - s[i-8] >= r[i] (8 <= i <= 23)
    //3、s[23] + s[i] - s[i+16] >= r[i] (0 <= i <= 7)
    
    //第一个不等式s[i]-s[i-1]表示答案中第i个小时有多少个出纳员
    //第二个不等式s[i]-s[i-8]表示出纳员工作的时间段有第i-8小时的人数
    //第三个不等式和第二个一样的意思,只是这时是跨过一天,比如工作的
    //的时间段有包括第1个小时的出纳员数即为 s[23]+s[1]-s[1+16]
    
    //从上面的式子看,根据差分约束,我们要找出像求最短(长)路时的
    //三角不等式:d[to] - d[from] >= w
    //我们要转化为相同的样式,也就是不等号
    //方向要相同,于是:上面3个式子就第一个式子需要转化,转化为
    //s[i] - s[i-1] >= 0 和 s[i-1]-s[i] >= -w[i]
    //这样子就变成4个不等式了
    //接下来的难点就是最后一个式子有个s[23]这个既是我们所要求的答案
    //这里又要当做常数(因为它的下标是固定的,当做常数比较好处理,而且
    //按照式子把s[23]当做常数,跟上面的样式才一样,都有变量i),
    //这样我们就可以把s[23]当做常数移到右边去,然后枚举所有s[23]可能取到
    //的值(0 <= s[23] <= 总的应聘人数m),这里也可以用二分,不过我们尝试过
    //然后就可以根据三角不等式d[to]-d[from]>=w 建一条from 到 to的边
    //因为差分约束是求单源最短路,所以要建一个源点,我把源点的下标设为24
    //跟源点连边的就只要根据第1个式子跟0点建边即可(s[-1] - s[0] >= w[0])
    //这里s[-1]极为源点s[24],从0指向24
    //求出来的dis数组中每个数就是有经过各个时间的的出纳员总数
    //我们用的是 >= 所以我们在求最短路是要维护好,即若s[to] - s[from] < w[i]
    //则要把s[to]维护为s[to] = s[from] + w[i] 这样就保持了>=号
    //其他的看代码中的注释
    
    //看不懂的话可以到这来看看,我也是看这的
    //他写的解释不错,不过跟人觉得他代码风格不好,还用goto
    //http://www.cnblogs.com/zhuangli/archive/2008/07/26/1252252.html
    
    #define comein freopen("in.txt", "r", stdin);
    #include <stdio.h>
    #include <string.h>
    #include <queue>
    using namespace std;
    
    #define INF 1<<30
    #define N 30
    
    int hour_least[N], app_st[N], hour_most[N];
    //count array is to count the time of a point have been visited
    int head[N], relate23[N], dis[N], count[N];
    int eid, eid23, source_id;
    bool vis[N];
    
    struct EDGE
    {
        int to, dis, next;
    }edge[1005];
    
    void buildMap() //build map
    {
        eid = 0;
        memset(head, -1, sizeof(head));
        for(int i = 1; i < 24; ++i)
        {   //s[i] represent sum of employees at before i hour
            //so s[23] is the answer which us want to get
            //0 <= s[i] - s[i-1] <= most[i] (0 <= i <= 23)
    
            //build edge from i-1 to i for s[i] - s[i-1] >= 0
            edge[++eid].to = i;
            edge[eid].dis = 0;
            edge[eid].next = head[i-1];
            head[i-1] = eid;
    
            //build edge from i to i-1 for s[i-1] - s[i] >= -most[i]
            edge[++eid].to = i-1;
            edge[eid].dis = -hour_most[i];
            edge[eid].next = head[i];
            head[i] = eid;
    
            if(i >= 8)   //s[i] - s[i-8] >= r[i] (8 <= i <= 23)
            {   //r[i] represent at i hour need at least r[i] employees
                edge[++eid].to = i;
                edge[eid].dis = hour_least[i];
                edge[eid].next = head[i-8];
                head[i-8] = eid;
            }
        }
    
        //s[i-1] - s[i] >= -Num[i] (0 <= i <= 23)   这里取i=0
        //s[-1] - s[0] >= -Num[0]   这里的Num相当于hour_most
        //from 24 to 0
        edge[++eid].to = 24;
        edge[eid].dis = -hour_most[0];
        edge[eid].next = head[0];
        head[0] = eid;
    
        for(int i = 0; i < 24; ++i)
        {   //s[23] + s[i] - s[i+16] >= r[i] (0 <= i <= 7)
            if(i <= 7)
            {   //build edge from (16、17...23) to (0、1、2...7)
                edge[++eid].to = i;
                edge[eid].dis = hour_least[i] - 0; //assume s[23] zero
                edge[eid].next = head[i+16];
                head[i+16] = eid;
                relate23[i] = eid; //mark the id which relate to s[23]
            }
            //Add a source point 24
            //build edge from source to i, and value of edge is zero
            edge[++eid].to = i;
            edge[eid].dis = 0;      //24到23的距离最长先设为0
            edge[eid].next = head[24];
            head[24] = eid;
            if(i == 23)
                source_id = eid;
        }
    }
    
    bool spfa()
    {
        for(int i = 0; i < 25; ++i)
        {
            vis[i] = false;
            dis[i] = -(INF);
            count[i] = 0;
        }
        dis[24] = 0;    //这个记得赋值为0,刚开始一直没找到错误,原来是这里
        queue<int>que;
        que.push(24);
        vis[24] = true;
        while(!que.empty())
        {
            int now = que.front();
            que.pop();
            for(int i = head[now]; i != -1; i= edge[i].next)
            {
                int to = edge[i].to;
                if(dis[to] - dis[now] < edge[i].dis)
                {
                    dis[to] = dis[now] + edge[i].dis;
                    if(vis[to] == false)
                    {
                        vis[to] = true;
                        count[to]++;
    
                        //判断负环,若进队次数比边数还多,那就说明有负环
                        if(count[to] > eid)
                            return false;
                        que.push(to);
                    }
                }
            }
            vis[now] = false;
        }
        return true;
    }
    
    void find(int n_app)
    {
        int sum = 0;
        bool is_find = false;
        while(is_find == false && sum <= n_app)
        {   //as from point i to i+16 distanse is r[i]-s[23]
            //here r[i] equal to hour_least[i] and s[23] equal to sum
            //if s[23] too small, there will have a loop when spfa()
            for(int i = 0; i <= 7; ++i) //这里与最后一个不等式相关的边都要更新
                edge[relate23[i]].dis = hour_least[i] - sum;
            //这里要更新,当最长路和sum,即从24到23的距离跟 从24到23的最长路相等
            //时就表示找到答案了
            edge[source_id].dis = sum;  //here is most important
    
            is_find = spfa();   //寻找最长路
    
            //听说这里sum 不能等于4 不知道为什么 不过没有加上 ||sum == 4
            if(is_find == false || dis[23] != sum)//也可以过
            {
                is_find = false;
                sum++;
            }
        }
        if(is_find == true)
            printf("%d\n", sum);
        else
            puts("No Solution");
    }
    
    int main()
    {
        int n_case;
        scanf("%d", &n_case);
        while(n_case--)
        {
            for(int i = 0; i < 24; ++i)
            {
                hour_most[i] = 0;    //记录
                scanf("%d", &hour_least[i]);
            }
            int n_app;  //number of applicants
            scanf("%d", &n_app);
            for(int i = 0; i < n_app; ++i)
            {
                scanf("%d", &app_st[i]);
                //the most applicant can reach to i hour
                hour_most[app_st[i]]++;
            }
            buildMap();
            find(n_app);
        }
        return 0;
    }
  • 相关阅读:
    SSL 1579——泽泽在巴西
    SSL 1644——取数字问题
    SSL 1589——火车票
    SSL 1506——打鼹鼠
    SSL 1212——大厅安排
    洛谷 1064——金明的预算方案(动态规划的背包问题)
    SSL 1463——公共子串
    SSL 1461——最大连续数列的和
    SSL 1643——最小乘车费用
    SSL 1460——最小代价问题
  • 原文地址:https://www.cnblogs.com/gabo/p/2619786.html
Copyright © 2011-2022 走看看