雇用计划

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;
}
原文地址:https://www.cnblogs.com/sun915/p/9512594.html