ACM解题总结-hihoCoder1137

(p.s:大过年的没啥事儿,做道题来解解闷儿~ )

题目来源:
    HihoCoder1137 

题目要求:
    公司招收X个男员工和Y个女员工,现给定N个候选人的信息:(X + Y ≤ N),每个员工的信息包括:性别、期望薪资和该员工的价值。公司也有总的薪资预算,记为B,即招聘的员工的薪资的总和不能多于B。要求通过程序计算得到合理的选择策略,使得招聘员工的总价值最高,同时总的薪资最少。

解答:
    很明显这是一道“背包问题”的题目,分别对男女员工用背包问题的策略进行求解即可。但和传统的背包问题相比,区别在于,这里明确给出了选择员工的个数(X个男员工,Y个女员工),而传统的背包问题仅仅关注的是背包内的总价值,而对于背包中物品的个数是没有限制的。因此这里需要对传统的背包策略进行细微的修改,解法如下:
    设:value[i][j][k]的值表示:在给出的前i个员工中选择j个、同时这j个员工的总的薪资为k的情况下所取得的最大价值。
    再设:person[i][j][k]的值表示:
给出的前i个员工中选择j个、同时 这j个员工的总的薪资为k 的情况下 给出的前i个员工中选择j个、同时 这j个员工的总的薪资为k 的情况下取得最大价值时,所选择的编号最大的员工的编号。如果没有对应的方案,即从前i个员工选择j个时,无论如何选择,总的薪资都不可能为k时,person[i][j][k] = -1。
    分别计算男、女员工的选择策略。这时,在给定条件下对男、女员工的选择可以取得的最大的价值就是:value[male_count][X][B] 和 value[female_count][Y][B]的值。这里:male_count和 female_count分别代表给出的候选人中男、女员工的个数,同时:X ≤ male_count、Y ≤ female_count。
    显然:当j = k = 0时,不论i的值是多少,value[i][j][k]的值是0,同时person[i][j][k]的值是0,员工的编号从1开始,person[i][j][k] = 0表示,不选择任何员工,这是一种合法的情况,和非法情况的值-1所代表的意义是不同的。下面用动态规划的思想解决更平凡的value[i][j][k]和person[i][j][k]。这里采用的就是背包问题的解决思路:对于任意的i,j,k值:value[i][j][k]和person[i][j][k]的值的计算采用下面的策略:
    从前i个员工中选择j个,同时总的薪资为k,这里有两种选择的方式:选择编号为i的员工或不选择编号为i的员工。下面的情况讨论中,员工i的价值和薪资分别用v(i)和c(i)表示。
    如果不选择编号为i的员工,那么这意味着要从前i-1个员工中选择j个,同时总薪资为k,这时可以获取的最大价值就是value[i - 1][j][k],选择的最大编号的员工就是person[i - 1][j][k]。
    如果选择编号为i的员工,那么这意味着要从前i-1个员工中选择j - 1个,同时这j - 1个员工的总薪资为k - cost(i)。这时,获得的最大价值是:value[i - 1][j - 1][k - c(i)] + v(i),而选择的员工中编号最大的就是i。注意,这种情况合法的前提是value[i - 1][j - 1][k - c(i)是合法的,即:
                person[i - 1][j - 1][k - c(i)] ≠ -1
    因此,就保证最大总价值而言,选择的方式就是上面两种情况的较大者:即:
       value[i][j][k] = max{value[i - 1][j][k], value[i - 1][j - 1][k - c(i)] + v(i)}
       person[i][j][k] = person[i - 1][j][k]  (上式的结果是前者)
                       或:
                       = i (上式中选择了后者)。
    这样,按上面的规则分别计算男女员工的选择情况,就可以得到相关的value和person矩阵。男女员工的矩阵分别记为:m_value、m_person 和 female_value 和 female_person。
    
    但题目的要求是男女员工的薪资总和不能超过预算B,因此需要决定招聘男女员工的总薪资的预算分别是多少,这里分别记为 B_male 和 B_female,因此:B_male + B_female ≤ B。

    这里采取一种最简单的策略来决定预算B对于男女员工的分配:枚举B_male 从0到B,对于每一个B_male的值,B_female 的值就是 B - B_male,在 m_person[male_count][X][B_male] 和 f_person[female_count][Y][B - B_male] 的值均不等于-1时,找到使得m_value[male_count][X][B_male] + f_value[female_count][Y][B - B_male]取得最大值时的B_male值,就得到了最优的招聘男女员工薪资预算分配的方式。

    现在讨论一下题目中最小化招聘员工的总薪资的问题。记上面最优预算分配中男员工的薪资预算为B_opt,那么对于女员工的薪资预算就是B - B_opt。同样采用枚举的方式,依次查看m_value[male_count][B_opt]、
m_ value[male_count][B_opt - 1]、 m_ value[male_count][B_opt - 2]、...、 m_ value[male_count][0],如果查看到某个值 m_ value[male_count][B_opt - i] 比  m_ value[male_count][B_opt - i + 1]小,则说明从 B_opt - i + 1 在减少薪资预算,就会影响到总的员工价值,说明在保证总价值最大的情况下,最小的薪资支出为 B_opt - i + 1。同样女员工招聘时的最小薪资支出也由f_ value[female_count][B - B_opt]依次减小薪资预算值来找到。记男女员工的最小薪资支出是 B_male_opt 和 B_female_opt。
    
    此时,就可以得到采取最优的招聘方式时,得到的最大价值是m_value[male_count][X][B_male_opt] + f_value[female_count][Y][B_female_opt]。 而需要支出的最小薪资总和为:B_male_opt + B_female_opt。

    最后说明一下如何找到选择哪些员工,这里用到的是person矩阵,由于矩阵中的元素是每种情况下选择的编号最大的员工的编号,因此,查看m_person[i][j][k]的值就可以找到其中一个员工的编号,接着查看m_person[m_person[i][j][k]][j - 1][k - c[m_person[i][j][k]]就可以再找到一个员工的编号(注意:这里的i值不是简单地减1,因此m_person[i][j][k] 不一定大于m_person[i - 1][j - 1][k - m_person[i][j][k]],直接用
m_person[i - 1][j - 1][k - m_person[i][j][k]]可能会导致同一个员工的多次选择,这个bug卡了我很久 ),当m_person[i][j][k]的值为0或者-1时,则表明所有选择的员工编号都已得到。
    从m_person[male_count][X][B_male_opt] 按照上面的方法推算,就可以找到所有的需要选择的男员工,对于女员工的选择方式也类似,从f_person[female_count][Y][B_female_opt]开始寻找即可。

    以上是对题目的解析。

输入输出格式:
     输入:第1行:整数N、X、Y、B,分别代表候选人个数、计划招聘男员工个数、计划招聘女员工个数、薪资总额预算;接下来的N行,每行描述一个候选人的信息,信息包括:G、V、S,本别表示每一个候选人的性别、价值、薪资。
    输出:第1行:输出2个整数:总的价值和总薪资支出;第2行:输出每一个选择的员工的编号,编号升序排列。

数据范围:
    
1 <= N <= 100
         0 <= X <= N
         0 <= Y <= N
         1 <= X + Y <= N
         1 <= B <= 1000
         
1 <= V <= 10000
         0 <= S <= 10
 

程序代码:


/****************************************************/
/* File        : hiho_week_83.cpp                   */
/* Author      : Zhang Yufei                        */
/* Date        : 2016-02-03                         */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/


/*
 * Update log:
 *		Create by Zhang Yufei in 2016-02-01.
 *		Edit by Zhang Yufei in 2016-02-02.
 *			Submit: WA/RTE.
 *		Edit by Zhang Yufei in 2016-02-03.
 *			Submit: WA/AC.
 */
 
#include<stdio.h>
#include<stdlib.h>


/*
 * The structure to store the candidates' data.
 * Parameters:
 *		@id: The id of the candidate.
 *		@sex: The gender of candidate.
 *		@value: The well-estimated ability value of this candidate.
 *		@salary: The expect salary of this candidate.
 */
typedef struct node1 {
	int id;
	char sex;
	int value;
	int salary;
} candidate;


/*
 * The element of the matrix.
 * Parameters:
 *		@value: The sum value;
 *		@last: The number of the last candidate to choose.
 */
typedef struct node2 {
	int value;
	int last;
} element;


/*
 * The matrix used in kinapsack problem. The element matrix[i][j][k]
 * means that the total value and the id of the last candidats when 
 * choose j candidates from the first i candidates and the total cost is k.
 */ 
element male_matrix[101][101][1001];
element female_matrix[101][101][1001];


/*
 * Record the mininum sum salary when get the maxinum value in the given budget.
 */
int male_cost[1001];
int female_cost[1001];


/*
 * The stack used to store result.
 */
int male_result[101];
int female_result[101];
int male_top = 0;
int female_top = 0;


/*
 * The main Program.
 */
int main(void) {
	int N, X, Y, B;
	scanf("%d %d %d %d", &N, &X, &Y, &B);
	
	candidate *male[N + 1];
	candidate *female[N + 1];
	int male_count, female_count;
	
	candidate *input;
	male_count = female_count = 0;
	
	for(int i = 1; i <= N; i++) {
		input = (candidate*) malloc(sizeof(candidate));
		getchar();
		scanf("%c %d %d", &input->sex, &input->value, &input->salary);
		input->id = i;
		if(input->sex == 'M') {
			male[male_count + 1] = input;
			male_count++;
		} else {
			female[female_count + 1] = input;
			female_count++;
		}
	}
	
	for(int i = 0; i < 101; i++) {
		for(int j = 0; j < 101; j++) {
			for(int k = 0; k < 1001; k++) {
				male_matrix[i][j][k].value = female_matrix[i][j][k].value = 0;
				if(j == 0 && k == 0) {
					male_matrix[i][j][k].last = female_matrix[i][j][k].last = 0;
				} else {
					male_matrix[i][j][k].last = female_matrix[i][j][k].last = -1;
				}
			}
		}
	}
	
	for(int i = 0; i < 1001; i++) {
		male_cost[i] = female_cost[i] = -1;
	}
	
	for(int i = 1; i <= male_count; i++) {
		for(int j = 1; j <= X; j++) {
			for(int k = 0; k <= B; k++) {
				male_matrix[i][j][k].value = male_matrix[i - 1][j][k].value;
				male_matrix[i][j][k].last = male_matrix[i - 1][j][k].last;
				
				if(k - male[i]->salary >= 0 && male_matrix[i - 1][j - 1][k - male[i]->salary].last != -1 
				 &&	male_matrix[i - 1][j - 1][k - male[i]->salary].value + male[i]->value >
					male_matrix[i - 1][j][k].value) {
						male_matrix[i][j][k].value = 
							male_matrix[i - 1][j - 1][k - male[i]->salary].value + male[i]->value;
						male_matrix[i][j][k].last = i;
				}
			}
		}
	}
	
	for(int i = 1; i <= female_count; i++) {
		for(int j = 1; j <= Y; j++) {
			for(int k = 0; k <= B; k++) {
				female_matrix[i][j][k].value = female_matrix[i - 1][j][k].value;
				female_matrix[i][j][k].last = female_matrix[i - 1][j][k].last;
				
				if(k - female[i]->salary >= 0 && female_matrix[i - 1][j - 1][k - female[i]->salary].last != -1
				 &&	female_matrix[i - 1][j - 1][k - female[i]->salary].value + female[i]->value >
					female_matrix[i - 1][j][k].value) {
						female_matrix[i][j][k].value = 
							female_matrix[i - 1][j - 1][k - female[i]->salary].value + female[i]->value;
						female_matrix[i][j][k].last = i;
				}
			}
		}
	}
	
	if(male_matrix[male_count][X][0].last != -1) {
		male_cost[0] = 0;
	}
	
	for(int i = 1; i <= B; i++) {
		male_cost[i] = male_cost[i - 1];
		if(male_cost[i - 1] != -1 &&
			male_matrix[male_count][X][male_cost[i - 1]].value < male_matrix[male_count][X][i].value
			|| male_cost[i - 1] == -1 && male_matrix[male_count][X][i].last != -1) {
			male_cost[i] = i; 
		}	
	}
	
	if(female_matrix[female_count][Y][0].last != -1) {
		female_cost[0] = 0;
	}
	
	for(int i = 1; i <= B; i++) {
		female_cost[i] = female_cost[i - 1];
		if(female_cost[i - 1] != -1 && 
			female_matrix[female_count][Y][female_cost[i - 1]].value < female_matrix[female_count][Y][i].value
			|| female_cost[i - 1] == -1 && female_matrix[female_count][Y][i].last != -1) {
			female_cost[i] = i; 
		}	
	}
	
	int max_value = 0;
	int cost_partition;
	for(int i = 0; i <= B; i++) {
		if(male_cost[i] != -1 && female_cost[B - i] != -1 &&
			max_value < male_matrix[male_count][X][male_cost[i]].value +
			female_matrix[female_count][Y][female_cost[B - i]].value) {
			max_value = male_matrix[male_count][X][male_cost[i]].value +
				female_matrix[female_count][Y][female_cost[B - i]].value;
			cost_partition = i;		
		}
	}
	
	printf("%d %d\n", male_matrix[male_count][X][male_cost[cost_partition]].value + 
		female_matrix[female_count][Y][female_cost[B - cost_partition]].value, 
		male_cost[cost_partition] + female_cost[B - cost_partition]);
	
	int i = male_count;
	int j = X;
	int k = male_cost[cost_partition];


	while(i >= 0 && j >= 0 && k >= 0 && male_matrix[i][j][k].last != -1
		&& male_matrix[i][j][k].last != 0) {
		male_result[male_top++] = male[male_matrix[i][j][k].last]->id;
		int i0 = i;
		int j0 = j;
		int k0 = k;
		k -= male[male_matrix[i0][j0][k0].last]->salary;
		i = male_matrix[i0][j0][k0].last - 1;
		j--;
	}
	
	i = female_count;
	j = Y;
	k = female_cost[B - cost_partition];
	
	while(i >= 0 && j >= 0 && k >= 0 && female_matrix[i][j][k].last != -1 
		&& female_matrix[i][j][k].last != 0) {
		female_result[female_top++] = female[female_matrix[i][j][k].last]->id;
		int i0 = i;
		int j0 = j;
		int k0 = k;
		k -= female[female_matrix[i0][j0][k0].last]->salary;
		i = female_matrix[i0][j0][k0].last - 1;
		j--;
	}	
	
	i = male_top - 1;
	j = female_top - 1;
	while(i >= 0 && j >= 0) {
		if(male_result[i] < female_result[j]) {
			printf("%d ", male_result[i]);
			i--;
		} else {
			printf("%d ", female_result[j]);
			j--;
		}
	}
	
	while(i >= 0) {
		printf("%d ", male_result[i]);
		i--;	
	}
	
	while(j >= 0) {
		printf("%d ", female_result[j]);
		j--;
	}
	printf("\n");
	
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值