例题9-15 校长的烦恼 UVa10817

1.题目描述:点击打开链接

2.解题思路:本题利用集合上的动态规划解决。定义集合s1表示恰好有一个人教的课程,集合s2表示至少有两个人教的课程。定义d(i,s1,s2)表示已经考虑了前i个人时的最小花费(人物编号从0开始)。则不难写出状态转移方程:

d(i,s1,s2)=min{d(i+1,s1‘,s2‘)+c[i],d(i+1,s1,s2)};

上式中只有当i≥m时才会考虑第二项。对于这个方程的理解,重点在s1,s2集合的变化,随着s2集合不断扩大,花费的资金也应当越少,当s2恰好等于所有科目的集合时,不需要花钱,i只是对阶段的描述。其实这也不难理解,没有一个教师来教书的时候(即i=0时),肯定是学校要花钱最多的时候,当所有的教师都应聘完毕了,课程都有人教了,自然花费就少了。

本题的一个重要技巧就是利用位运算来解决各种集合之间的关系,另外一个小技巧就是设置一个辅助的参数s0,来方便集合之间的运算:将d(i,s1,s2)改为d(s0,s1,s2),其中s0表示没有任何人能教的科目的集合。对于方程中s1‘,s2‘的计算,详见代码。注意:由于本题中的课程是从1开始编号的,而集合中是从0开始处理的,因此在输入时要预处理。

3.代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

const int maxn = 120+10;
const int maxs = 8 + 1;
#define INF 100000000
int m, n, s;
int c[maxn], st[maxn];//数组c表示工资,st表示第i个老师教课的集合
int d[maxn][1 << maxs][1 << maxs];

int dp(int i, int s0, int s1, int s2)
{
	if (i == m + n)
		return s2 == (1 << s) - 1 ? 0 : INF;//如果s2恰好等于全部课程的集合时,已经满足题意,不需要花钱
	int&ans = d[i][s1][s2];
	if (ans >= 0)return ans;
	ans = INF;
	if (i >= m)ans = dp(i + 1, s0, s1, s2);//不选第i个应聘者,由于选i应聘者会导致s0,s1,s2改变,因此先初始化成不选
	int m0 = st[i] & s0;//只有第i个应聘者会教的课程
	int m1 = st[i] & s1;//第i个应聘者也会教的课程
	s0 ^= m0;//在s0集合中除去所有只有i应聘者会教的课程,即m0
	s1 = (s1^m1) | m0;//m1代表的所有课程变为了至少两个人会教,从s1中除去,同时加上m0
	s2 |= m1;//将m1添加到s2
	ans = min(ans, c[i] + dp(i + 1, s0, s1, s2));//选第i个应聘者,取较小者
	return ans;
}

int main()
{
	//freopen("test.txt", "r", stdin);
	while (~scanf("%d%d%d", &s, &m, &n) && s&&m&&n)
	{
		memset(d, -1, sizeof(d));
		memset(st, 0, sizeof(st));
		memset(c, 0, sizeof(c));
		getchar();
		for (int i = 0; i < m + n; i++)
		{
			string str;
			getline(cin, str);
			stringstream ss(str);
			int x,flag=1;
			while (ss >> x)
			{
				if (flag){ flag = 0; c[i] = x; }
				else
				{
					x--;
					st[i] |= (1<<x);
				}
			}
		}
		int ans = dp(0, (1 << s) - 1, 0, 0);
		printf("%d\n", ans);
	}
	return 0;
}
时间: 2024-11-07 14:39:56

例题9-15 校长的烦恼 UVa10817的相关文章

10817 - Headmaster&#39;s Headache(校长的烦恼)

经典的状态压缩DP . 根据DP的阶段定义,我们需要枚举每一个教师进行递推,但是由于每个教师可以教授的课程是复杂多样的,所以使得状态变得难以转移 .那么要怎么样表示状态呢? 显然增加一两个维度是无法胜任的,所以我们可以用二进制枚举子集的方法,用一个整数通过位运算充当一个集合 . C++提供的位运算符极像对集合的操作,我们恰好可以利用这一点 . 用d[i][s1][s2]表示考虑了前i个人时的最小花费 . 其中s1表示恰有一个人教的科目集合,s2表示至少有两个人教的科目集合 .因为由这两个量就可以

UVa 10817 校长的烦恼

https://vjudge.net/problem/UVA-10817 题意: 某校有m个教师和n个求职者,需讲授s个课程,已知每人的工资c和能教的课程集合,要求支付最少的工资使得每门课都至少有两名老师能教. 思路: s1表示恰好有一个人教的科目集合,s2表示至少有两个人教的科目集合. d[i][s1][s2]表示已经考虑了前i个人时的最小花费.参考了大神的代码,如下: 1 #include<iostream> 2 #include<string> 3 #include<c

例题1.15 网络 UVALive 3902

1.题目描述:点击打开链接 2.解题思路:本题要求放置尽可能少的服务器,使得所有的客户端到最近的服务器的距离都不超过k.由于已经放置了一个服务器,不妨把它当做根结点,先把无根树转化为有根树,然后我们考虑最深的叶子,那么不难证明,该叶子结点的最优服务器的放置位置是它的k级祖先.这样本题的算法便不难想出:从最深的叶子开始枚举,并在它的k级祖先处放置一个服务器,同时标记该服务器能覆盖到的所有结点,这样当所有的叶子都被覆盖到时,服务器数量就是最少的.只用枚举到第k+1层叶子即可停止,因为小于等于k的叶子

例题3.15 子串 UVa11468

1.题目描述:点击打开链接 2.解题思路:本题利用概率dp+AC自动机解决.首先,把所有的模板串加入到Trie,然后标记所有单词结点,然后每次随机生成一个字符,就相当于在AC自动机中随机走一步,而且只允许走不被标记的结点.令d(u,L)表示当前在结点i,还需要走L步,不进入任何禁止结点的概率.那么不难由全概率公式得到下式: d(u,L)=sum{P[v]*d(v,L-1)|v是一个没有被禁止的结点}: 这样,最终的答案就是d(0,L). 3.代码: #include<iostream> #in

10118 - Free Candies(DP)

自己想出来的,一遍AC,0.132s 说说我的思路吧,首先考虑如何表示状态,不难发现,情况非常多,因为怎么拿都行,所以只好增加维度,开四维数组. 由于结构比较无序,所以选择了记忆化搜索 ,受前面<校长的烦恼>的启发,即使我们还要维护篮子中糖果情况,但是我们只需开四维数组就已经足够表示所有的状态了 ,我们大可以将篮子中的情况以及口篮子中糖果的数量放在函数的参数中来维护就可以了,那么我们不难用d[a][b][c][d]表示各堆糖果取到当前状态时口袋中糖果的最大对数 . 如何维护当前篮子中糖果的颜色

训练指南DP阶段训练1

最近又忙又颓.............时间抓不紧....下学期开始就要准备考研了.......就2个月左右可以做自己喜欢的事了....争取把紫书和白书没做的,做过的..来一次完整的总结 训练指南上面的5个例题+后面15个习题是第一阶段 vjudge训练地址 http://vjudge.net/contest/139533#overview -------------------------------------------------------------------------------

复杂状态的动态规划

紫皮各种…… 1.最优配对问题 d(i,S) = min{|Pi,Pj| + d(i-1,S-i-j) | j在S中}; 空间 n 个点,配成n/2对使两点的距离之和尽量少 for(int i = 1;i <= n;i++){ for(int S = 0;S < (1 << n);S++){ d[i][S] = INF; for(int j = 0;j < n;j++){ if(S & (1<< j)){ /// 如果j能参与配对 d[i][S] = mi

C/C++算法竞赛入门经典Page11 例题1-5 三整数排序

题目:输入3个整数,从小到大排序后输出 样例输入: 20 7 33 样例输出: 7 20 33 首先,先声明三个整数a,b,c和一个临时变量t: int a,b,c,t;//1,b,c为三个整数,t为临时变量 输入三个整数: scanf("%d%d%d",&a,&b,&c); 进行3次if判断: 1.如果b>a,则a,b对调 2.如果c>a,则a,c对调 3.如果c>b,则b,c对调 代码: if(b>=a){ t=b; b=a; a=t

【转】科大校长给数学系学弟学妹的忠告&amp;本科数学参考书

1.老老实实把课本上的题目做完.其实说科大的课本难,我以为这话不完整.科大的教材,就数学系而言还是讲得挺清楚的,难的是后面的习题.事实上做1道难题的收获是做10道简单题所不能比的. 2.每门数学必修课至少要看一本参考书,尽量做一本习题集. 3.数学分析别做吉米,除非你太无聊,推荐北大方企勤的习题集.此外注意一下有套波兰的数学分析习题集,是不是搞得到中文或英文版. 4.线性代数推荐普罗斯库列科夫的<<线性代数习题集>>和法捷耶夫的<<高等代数习题集>>.莫斯科