UVA10817

才学了状压dp就赶紧来做一道,结果这道题卡了我两天才过...

关键是这道题的状态转移方程,我看了紫书上的大概思路才写出来。

用s1,s2表示集合,分别为一个老师上的课程,两个老师上的课程。

那么d[s1][s2]=min(d[s1^a][s2^b]+请这个老师用的钱)其中a,b分别是这个老师如果请了,那么会出现的一个老师上的课和两个老师上的课。

刚刚写的时候只用了i,s1,s2三个参数,发现不方便表示a,b所以加了一个s0,表示没有选的课程,即一个老师也没有的课程。

代码和注释如下:

#include
#include
#include
#include
#include
#include
#define up(i,a,b)  for(int i=a;ib;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const long long INF = 0x3f3f3f3f;
using namespace std;
int m, s, n;
int	d[1000][(1<<8)+1][(1<<8)+1];
int st[100000];
int my[100000];
int dp(int i, int s0, int s1, int s2)
{
	if (i == m + n)//这个函数代表要用的钱,当第i个老师请了的话
	{
		if (s2 == (1 << s) - 1)//如果s2已经满了那么就不要钱了不用请老师
			return 0;
		else return INT_MAX;
	}
	int &ans = d[i][s1][s2];
	if (ans >= 0) return ans;
	ans = INT_MAX;
	if (i >= m)  ans = dp(i + 1, s0, s1, s2);//如果不请第i个老师的话
	int m0 = st[i]&s0;//第i个老师特有的
	int m1 = st[i] & s1;//与只上一节课重复的,那么这节课变成了两个老师上课了
	s0 ^= m0;
	s1 = (s1^m1) | m0;//记得s1要去掉已经变成了两节课的课程
	s2 |= m1;
	ans = min(ans, dp(i + 1, s0, s1, s2)+my[i]);//如果请了,判断是否是最小的
	//cout << ans << endl;
	return ans;//返回结果
}
int main()
{
#ifdef local
	freopen("D:\\data.in.txt", "r", stdin);
	freopen("D:\\data.out", "w", stdout);
#endif 
	while (cin >> s >> m >> n && s&&m&&n)
	{
		memset(d, -1, sizeof(d));
		memset(my, 0, sizeof(my));
		memset(st, 0, sizeof(st));
		up(i, 0, m+n)//要注意一下这道题的输入
		{
			cin >> my[i];
			char ch;
			while ((ch = getchar()) != '\n')
			{
				if (isdigit(ch))
				{
					st[i] |= 1 << ((ch - '0')-1);
			//		cout << st[i] << endl;
				}
			}
		}
		cout << dp(0, (1<

 

 

 

你可能感兴趣的:(dp算法)