我做的第一道状态压缩dp。。。

这道题是看着大神的博客弄懂的。

状态压缩涉及到神奇的二进制。

& : 按位与

|   :按位或

^   :异或

1<<4 :2^4

a<<=4 : 变量a向左移4位(乘以2^4)


#include <iostream>
#include <string>
#include <cstring>

using namespace std;
const int MAX = 1 << 16;

struct Node
{ 
	int cost;     // 所需时间
	int pre;      // 前一状态
	int reduced;  // 最少损失分数
};
Node dp[MAX];
bool visit[MAX];
struct Course
{
	string name;
	int deadline;
	int cost;
};
Course course[16];

void output(int status)
{
	int curjob = dp[status].pre^status;  //找到当前是哪一项个状态作业
	int curid = 0;  //找到当前状态的作业到底是第几项
	curjob >>= 1;
	while (curjob)
	{
		++curid;
		curjob >>= 1;
	}
	if (dp[status].pre != 0)
	{
		output(dp[status].pre);  //递归
	}
	std::cout << course[curid].name << endl;
}

int main()
{
	int t; cin >> t;
	while (t--)
	{
		int n; cin >> n;
		int upper = 1 << n;
		int dayupper = 0;
		int i, j;
		for (i = 0; i < n;++i)
		{
			cin >> course[i].name >> course[i].deadline >> course[i].cost;
			dayupper += course[i].cost;
		}
		memset(visit, 0, sizeof(visit));
		dp[0].cost = 0;
		dp[0].pre = -1;
		dp[0].reduced = 0;  // dp[0]是指所有状态都没有做的状态
		visit[0] = 1;
		int work;
		int tupper = upper - 1;  // tupper表示二进制数是 n 个 1(所有作业都完成了);
		for (j = 0; j < tupper; ++j ) //遍历所有状态
		{
			for (work = 0; work < n;++work)  // n项作业一一枚举
			{
				int cur = 1 << work;    //把作业转换成二进制状态
				if ((cur&j)==0)         //该作业没有做过  
				{
					int curtemp = j|cur;  //在“j”状态的基础上,增加作业cur
					int day = dp[j].cost + course[work].cost;
					dp[curtemp].cost = day;
					int reduce = day - course[work].deadline;
					if (reduce < 0)
					{
						reduce = 0;
					}
					reduce += dp[j].reduced;  //在这里跪了一次
					if (visit[curtemp])
					{
						if (reduce<dp[curtemp].reduced)   //更新扣分的最小值
						{
							dp[curtemp].reduced = reduce;
							dp[curtemp].pre = j;
						}
					}
					else
					{
						visit[curtemp] = 1;
						dp[curtemp].reduced = reduce;
						dp[curtemp].pre = j;
					}
				}
			}
		}
		std::cout << dp[tupper].reduced << endl;
		output(tupper);  //递归输出
	}
	return 0;
}


dp路漫漫。。。

你可能感兴趣的:(我做的第一道状态压缩dp。。。)