zoukankan      html  css  js  c++  java
  • 雇用计划

    D14555. 雇拥计划

    时间限制:1.0s 内存限制:256.0MB
    输入文件名:test.in 输出文件名:test.out
    问题描述
      一位管理项目的经理想要确定每个月需要的工人,他当然知道每月所需的最少工人数。当他雇佣或解雇一个工人时,会有一些额外支出。一旦一个工人被雇佣,即使他不工作,他也将得到工资。这位经理知道雇佣一个工人的费用,解雇一个工人的费用和一个工人的工资。现他在考虑一个问题:为了把项目的费用控制在最低,他将每月雇佣多少个工人。
    输入格式
      共三行。第一行为月数n(不超过12)。
      第二行含雇佣一个工人的费用,一个工人的工资和解雇一个工人的费用(<=100)。
      第三行含n个数,分别表示每月需要的工人数(<=1000)。每个数据之间有一个空格隔开
    输出格式
      仅一行,表示项目的最小总费用。
    样例输入
    3
    4 5 6
    10 9 11
    样例输出
    199

    思路:

    先手暴力裸深搜,本来想找规律,结果苟到了60分,心想不会优化一下就过了吧蛤蛤蛤结果记忆化后果然过了。不过当时犯了一个小错误,dp数组开一维记录k月求得的最小值,但是状态中应当含有两个量月份和现有人数,略加修改。

    Code:

    记忆化搜索:

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int INF = 0x5f5f5f5f;
    int n;
    int h, p, f, ans = INF;
    int man[20], dp[20][2010];
    
    void dfs(int k, int cos, int tot) {
    	if (dp[k][tot] < cos) return;
    	dp[k][tot] = cos;
    	//if (cos > ans) return;
    	if (k > n){
    			//cout << cos << endl; 
    			if(cos < ans) ans = cos;
    			return;
    	}
    	if (man[k] < tot){
    		int gundan = tot - man[k];
    		for (int i = 0; i <= gundan; i++) {
    			int w = cos + (tot - i) * p + i * f;
    			dfs(k + 1, w, tot - i);
    		}	
    	}
    	if (man[k] >= tot){
    		int ps = man[k] - tot;
    		dfs(k + 1, cos + ps * h + man[k] * p, man[k]);
    	}
    }
    
    int main(){
    	freopen("test.in","r",stdin);
    	freopen("test.out","w",stdout);
    	memset(dp, INF, sizeof(dp));
    	cin >> n;
    	cin >> h >> p >> f;
    	for (int i = 1; i <= n; i++)
    		cin >> man[i];
    	dfs(1, 0, 0);
    	cout << ans << endl;
    	return 0;
    }
    

    然后思考贪心的方法。
    考虑一个人我们在明显需要他的情况下是不会让他流离失所的,所以不能乱解雇人。
    当现在工人的数量低于我们的需求时显然要招募工人,好处理EASY。

    问题在于工人数量溢出需求时的解雇需求。

    一个工人如果我们把他赶走之后叫回来的条件,是又缺工人的时候。如果我们不缺工人就要考虑让他滚蛋,但此时有存在把他叫回来的可能,因此应当分别计算“抛弃他再拯救他"和"包养他"的花费取最小值即可。

    我们暂且只需要考虑解雇到重雇这段时间内的两项花费即可。所以保养的时间是当前时间到之后第一个比他大的时间。
    注意我们要枚举一次解雇的人数。

    算法复杂度似乎是O(n^2)

    Code:

    贪心

    #include<bits/stdc++.h>
    
    using namespace std;
    
    int n, ans, tot = 0;
    int man[20];
    int hi, em, fi;
    
    void readp(){
    	cin >> n;
    	cin >> hi >> em >> fi;
    	for (int i = 1; i <= n; i++)
    		cin >> man[i];
    }
    
    void work(){
    	ans = 0;
    	tot = 0;
    	for (int i = 1; i <= n; i++) {
    		ans += man[i] * em;//良心老板先发工资。
    		if (man[i] >= tot) {
    			int com = man[i] - tot;
    			ans += com * hi;
    			tot = man[i];
    			continue;
    		}//缺人直接叫人
    		int hop = tot - man[i];
    		for (int j = 1; j <= hop; j++) {//枚举解雇人的数量。
    			int we = n + 1;//注意这里的初值不能乱赋,因为可能最大值就是当前点,从而导致保养时间计算出错(这是题解上看到的)
    			int extra = fi;
    			int charity = 0;
    			for (int k = i + 1; k <= n; k++)
    				if (man[k] >= man[i] + j) we = min(we, k);
    			if (we <= n) extra += hi;
    			charity = em * (we - i);
    			if (charity < extra) ans += em;
    			else ans += fi, tot -= 1;
    		}//然后分别计算价格然后比较即可
    	}
    	cout << ans << endl;
    }
    
    int main() {
    	freopen("test.in","r",stdin);
    	freopen("test.out","w",stdout);
    	readp();
    	work();
    	return 0;
    }
    
  • 相关阅读:
    AJAX
    Aliyun服务器配置Redis
    Aliyun服务器配置MySQL
    Python基础之迭代器详解
    Python基础之函数
    Flask入门--URL
    认识Web
    肖知兴:企业的底层逻辑与企业家的突破(下)
    建造者模式(Bulider模式)详解
    为什么我强烈推荐你用枚举来实现单例模式
  • 原文地址:https://www.cnblogs.com/sun915/p/9512594.html
Copyright © 2011-2022 走看看