UVa 10817 Headmaster's Headache

本文探讨了一种算法在解决课程分配问题上的应用,通过分析申请人和现有课程的情况,计算出在有限资源下最优化的课程分配方案,旨在减少教学成本并提高效率。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <sstream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
#define inf (1<<30)

// 申请人结构体
typedef struct node
{
	int cost;	// 薪水
	vector<int> sub;	// 可以教的课程编号		
}node;

// 申请人列表
node array[110];

int sub_num, now_num, app_num;

// 现在课程情况, subject[i]代表第i门课上课老师人数
int subject[10];

// record[i][S]代表在前i个申请人,在课程情况为S(第i位为j代表第i门课还需要j个老师)中选择老师满足条件的最少花费
int record[110][20000];

// 代表选择现在老师的花费
int now_cost;

int get_min(int x, int s);

int main()
{
	string str;
	while(getline(cin, str))
	{
		memset(record, -1, sizeof(record));
		memset(subject, 0, sizeof(subject));
		now_cost = 0;

		stringstream scin(str);
		scin >> sub_num >> now_num >> app_num;

		if(sub_num == 0)
			break;
		// 读入现在的老师情况
		for(int i = 1; i <= now_num; i++)
		{
			getline(cin, str);
			stringstream scin(str);
			int c;
			scin >> c;
			now_cost += c;
			while(scin >> c)
			{
				if(subject[c] < 2)
					subject[c]++;
			}	 		
		}

		// 读入申请者情况
		for(int i = 1; i <= app_num; i++)
		{
			getline(cin, str);
                        stringstream scin(str);
                        int c;
                        scin >> c;
                        array[i].cost = c;
			array[i].sub = vector<int>();
                        while(scin >> c)
				array[i].sub.push_back(c);	
		}

		// 计算现在课程情况的三进制表示
		int now_s = 0;
		for(int i = 1; i <= sub_num; i++)
			now_s = now_s*3 + (2-subject[i]);

//		printf("now_s: %d, now_cost: %d\n", now_s, now_cost);
		// 计算结果
		printf("%d\n", now_cost + get_min(app_num, now_s));	
	}		
	return 0;	
}

// 计算结果
// 在s课程情况,前x个申请人中选择老师的最少花费
int get_min(int x, int s)
{
//	printf("x: %d, s: %d \n", x, s);
	if(record[x][s] != -1)
		return record[x][s];
	// 如果没有需要上的课,返回0
	if(s == 0)		
	{
		record[x][s] = 0;
		return record[x][s];
	}

	// 如果没有可选申请人,返回无穷大
	if(x == 0)
	{
		record[x][s] = inf;
		return record[x][s];
	}

	// 分情况:选择该申请人,和不选该申请人。挑选其中较小的一方
	// 1.不选该申请人
	int r1 = get_min(x-1, s);
	
	// 2.选择该申请人
	// 计算新的课程情况
	
	int now_s[10];
	memset(now_s, 0, sizeof(now_s));
	int cumu_sum[10];
	memset(cumu_sum, 0, sizeof(cumu_sum));
	for(int i = 1; i <= sub_num; i++)
	{
		int num = s - cumu_sum[i-1];
		for(int j = 1; j <= sub_num-i; j++)
			num = num / 3;
		now_s[i] = num;
		cumu_sum[i] = cumu_sum[i-1] + num*pow(3,sub_num-i);					
	}	
/*
	int this_s[10];
	memset(this_s, 0, sizeof(this_s));
*/
	for(int i = 0; i < array[x].sub.size(); i++)
	{
		int k = array[x].sub[i];
		if(now_s[k] > 0)
			now_s[k]--;		
	}			

	int new_s = 0;
	for(int i = 1; i <= sub_num; i++)
		new_s = new_s*3 + now_s[i];

	int r2 = array[x].cost + get_min(x-1, new_s);

	record[x][s] = min(r1, r2);
//	printf("record[%d][%d]: %d\n", x, s, record[x][s]);
	return record[x][s];						
}


这题相对容易,用d(i, s)来代表在前i个申请人,在课程情况为s(第i位为j代表第i门课还需要j个教师上)下的最小花费

计算d(i, s)分两种情况:1.选第i个申请者,设第i个申请者薪水为cost(i). 需要根据第i个申请者能上的课更新s至s'. 2.不选第i个申请者

d(i, s) = min(d(i-1, s) , cost(i) + d(i-1, s'))


由于每个课只有三种情况:0,1,2. 所以仿照二进制表示的思路,可以三进制来表示s.


注意:当发现当前的状态表示很难时,就尝试增加状态的维度。对于复杂的集合状态,尝试用整数(某进制)来表示每一个元素情况。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值