#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.
注意:当发现当前的状态表示很难时,就尝试增加状态的维度。对于复杂的集合状态,尝试用整数(某进制)来表示每一个元素情况。
本文探讨了一种算法在解决课程分配问题上的应用,通过分析申请人和现有课程的情况,计算出在有限资源下最优化的课程分配方案,旨在减少教学成本并提高效率。

被折叠的 条评论
为什么被折叠?



