【贪心+优先队列】Working Plan

题目链接:传送门

题目描述

ICPC manager plans a new project which is to be carried out for n days. In this project, m persons numbered from 1 to m are supposed to work. Each day j (1 ≤ j ≤ n) requires dj persons, and each person i (1 ≤ i ≤ m) wants to work wi days.

To increase the efficiency in performing the project, the following two conditions should be satisfied:
    1.each person works for only consecutive w days when he/she works, and
    2.each person can work again after he/she has a rest for at least h days.
ICPC manager wants to find a working plan to assign the working days for all persons such that the number of working days of each person i (1 ≤ i ≤ m) is equal to wi and the number of persons who work for each day j (1 ≤ j ≤ n) is equal to dj, and above two conditions are also satisfied.

For example, assume the project is carried out for n = 9 days, and m = 4 persons participate in the project. Let w = 2 and h = 1. Also, assume (w1, w2, w3, w4) = (4, 4, 6, 2) and (d1, d2, d3, d4, d5, d6, d7, d8, d9) = (1, 3, 2, 1, 2, 1, 1, 3, 2). The table below shows a feasible solution where the i-th row corresponds to person i, and the j-th column corresponds to day j. If person i works or has a rest in day j, the value of the table element with row i and column j is 1 or 0, respectively.

Given m, n, w, h, wi (1 ≤ i ≤ m) which is a multiple of w, and dj (1 ≤ j ≤ n), write a program to find a feasible solution as a working plan.


输入

Your program is to read from standard input. The input starts with a line containing four integers, m, n, w, h (1 ≤ m ≤ 2,000, 1 ≤ n ≤ 2,000, 1 ≤ w, h ≤ n). The following line contains m integers where the i-th (1 ≤ i ≤ m) integer represents wi (1 ≤ wi ≤ n) which is a multiple of w. The next line contains n integers where the j-th (1 ≤ j ≤ n) integer represents dj (0 ≤ dj ≤ m).


输出

Your program is to write to standard output. If there is a feasible working plan, print 1 in the first line followed by m lines, each i-th (1 ≤ i ≤ m) line should contain wi/w integers. These integers form an increasing sequence of first days that person i works in the feasible plan. If there is no feasible working plan, print only -1 in the first line. The first sample below corresponds to the example given in the table above.


样例输入

样例数据

4 9 2 1
4 4 6 2
1 3 2 1 2 1 1 3 2

样例输出

1
1 8
2 7
2 5 8
4

【题意】:

有n天,m个工人,他们一旦工作就要连续工作w天,然后需要休息h天。

然后每个工人需要工作wi天,其中这个wi天是需要能被w天整除的。

最后是n天,每一天需要的工人数目。


【题解】:

其实贪心的题目写博客真的没有什么意思,但是这个题目非常具有代表性,这个题目就是代表着我们的操作系统中,进程调度,这一个环节。贪心进行即可。

把所有需要的人数的天数单独处理,用vis[i],表示,即第i天需要的工人数,因为题目给的那些是工人数,但是实际上每个工人都是连续工作多少天的,我们可以预处理这个数组出来。

处理完了,我们就知道1~n天每一天真正需要的人数对吧。

然后我们需要设立两个优先队列,

一个优先队列叫做 就绪队列Q,就是里面都是空闲的工人。

另外的一个优先队列叫做:阻塞队列tQ,里面都是已经派出去工作的工人,但是他们需要到某一个时间回来。

这个就是完整的一个进程调度的过程,看吧!!!

我们就是贪心处理,我们让就绪队列Q,按照工人的需要工作的天数排序,工作天数越大的排前面。

相对地,阻塞队列tQ,就需要按照工人回来下一次工作的时间进行排序。

然后我们就可以写出这个代码了

 

#include <stdio.h>
#include <string.h>
#include <map>
#include <algorithm>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const int N = 5e6+10;
const ll mod = 998244353;
const double eps = 1e-7;
int a[N],b[N],c[N],d[N],vis[N];

typedef struct Node{
    int No,val,t;
}Node;
typedef struct Cmp1{
    bool operator () (const Node &u,const Node &v)const {
        return u.t > v.t;
    }
}Cmp1;
typedef struct Cmp2{
    bool operator () (const Node &u,const Node &v)const {
        return u.val < v.val;
    }
}Cmp2;
Node w[N];
vector<int>v[2020];
int main()
{
    int n,m,h,W;
    scanf("%d%d%d%d",&m,&n,&W,&h);
    priority_queue <Node,vector<Node> ,Cmp2> Q;
    priority_queue <Node,vector<Node> ,Cmp1> tQ;
    for(int i=1;i<=m;i++){
        scanf("%d",&b[i]);
        w[i].No = i;
        w[i].val = b[i]/W;
        w[i].t = 0;
        Q.push(w[i]);
    }
    /*while(!Q.empty()){
        Node t = Q.top();
        Q.pop();
        printf("%d %d\n",t.No,t.val);
    }*/
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    int cnt = 0 ,flag=1;
    for(int i=1;i<=n;i++){

        if(a[i]!=0){
            int t=a[i];
            for(int j=1;j<=a[i];j++){
                //vis[i]++;
                d[cnt++] = i;
            }
            vis[ i ]+=a[i];
            for(int j=i;j<=i+W-1;j++)
                a[j]-=t;
        }
    }
    /*
    for(int i=0;i<cnt;i++){
        printf("%d%c",d[i],i==cnt-1?'\n':' ');
    }
    for(int i=1;i<=n;i++){
        printf("%d%c",vis[i],i==n?'\n':' ');
    }*/
    Node cur;
    for(int i=1;i<=n;i++){
        while( !tQ.empty() ){
            cur = tQ.top();
            if ( cur.t <= i ){
                Q.push(cur);
                tQ.pop();
            }else{
                break;
            }
        }
        while( !Q.empty() && vis[i] ){
            cur = Q.top();
            Q.pop();
            vis[i]--;
            cur.t = i + W + h;
            cur.val -- ;
            v[cur.No].push_back(i);
            if(cur.val!=0)
                tQ.push(cur);
        }
        if(vis[i]){
            flag=0;break;
        }
    }
    if(!flag){
        printf("-1\n");
    }else{
        printf("1\n");
        for(int i=1;i<=m;i++){
            int sz = v[i].size();
            int cnt =0;
            for(auto j :v[i]){
                cnt++;
                printf("%d%c",j,cnt==sz?'\n':' ');
            }
        }
    }
    return 0;
}
/*
4 14 3 2
9 6 9 6
2 3 3 2 1 2 2 2 1 2 3 4 2 1
*/

 

### 贪心算法基本概念 贪心算法是一种通过一系列的选择来解决问题的方法,在每一步选择中都做出当前状态下最好的选择,期望最终能够得到全局最优解。这种策略并不总是能获得最优化的结果,但在某些情况下确实可以找到最佳解决方案[^1]。 对于最小生成树问题而言,如果一个无向加权连通图被划分为两个集合A和B,那么连接这两个集合之间权重最小的边必定会成为构成最小生成树的一部分。这一特性使得Prim算法能够在构建过程中始终选取局部最优方案从而达到整体最优效果。 ### 优先队列与小根堆介绍 #### 优先队列定义 优先队列是一个抽象数据类型,它类似于常规队列或栈的数据结构,但是其中每个元素都有一个预先关联好的“优先级”。当访问、移除元素时,最先处理的是具有最高优先级别的那个元素;而插入操作则按照新加入项的实际价值自动调整其位置以维持内部顺序不变。 #### 小根堆概述 小根堆(Min Heap)是一类特殊的完全二叉树形式实现的优先队列,满足如下条件: - 对于任意节点i (除了根),`heap[i] >= heap[parent(i)]` 成立; - 新增元素通常放置到最后一层最右侧的位置上,并向上浮动直至恢复上述性质; - 删除最小值后需将最后一个叶子替换至顶部并向下沉降直到重新符合规则。 这些特点让小根堆非常适合用来高效管理动态变化中的极值查询需求场景下作为底层支撑工具之一[^2]。 ### 使用C++ STL库中的`priority_queue` 在实际编程实践中可以直接利用标准模板库提供的容器适配器——`std::priority_queue<T>` 来快速搭建起基于最大/最小堆逻辑运作机制下的先进先出(FIFO) 或者 后进先出(LIFO) 的自适应型序列化存储空间。默认配置下它是大顶堆行为模式,不过可以通过指定比较函数参数轻松切换成相反形态的小顶堆版本。 ```cpp #include <queue> // 定义一个小根堆 std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap; ``` ### 实现哈夫曼编码案例分析 考虑到字符频率分布情况各异可能导致不同长度前缀码表的存在,因此采用贪心思路配合优先队列技术可有效降低计算成本。具体做法是在初始化阶段把所有可能出现过的字母及其对应的出现次数封装成一个个独立对象存入到由小根堆维护着的一组候选列表里头去;之后不断取出概率最低两位组合起来形成新的综合体再放回原处继续参与后续竞争过程...如此这般循环往复直至最后只剩下一个超级节点为止。此时从这棵满二叉树顶端到底部路径所经过各分支方向上的0/1标记串接而成的就是相应叶节点代表的那个特定符号应该分配给它的唯一标识符了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值