题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1074
题目大意:
题意分析:
1、根据题意,我们需求所有课程的全排列情况的被罚分数,再从中选取最小的一种排列。虽然课程最多只有15门,但总共有15!种情况,这样做肯定会超时。
2、通过观察我们发现:假如我们把课程的选择当成一种状态的转移,初态时选择了零门课程,终态时所有课程选择完毕。则,总共会出现2^n种状态(n<=15,这在我们的计算能力之内)。
3、我们将这些状态用二进制方式进行存储,n位二进制数,每一位的值对应的课程的选择情况,1表示已经选择,0表示未选择。如状态5=101,表示选择了第1和第3门课。
4、有了状态这一概念之后,我们再将这些状态分类:选择了i门课的状态算作一类(0<i<=n),总共n类。
5、状态的转移实际上就是上一步中的“i”不断增加的过程,直到i=n。显然,每一次i增加我们需要在每一类状态中选择被罚分数最小的一种状态转移方式。举个状态转移的例子:假如有状态7(111),它的来源分为三种情况:状态6(110)+第1次作业;状态5(101)+第2次作业;状态3(011)+第3次作业,我们通过比较,选择被罚分数最小的情况,作为状态7(111).
6、通过5中的例子,我们不难发现,这种解题方式实际上是一种动态规划解题方式。我们有最优子结构:每一种状态就是一个最优子结构;我们也利用了重叠子问题,譬如状态转移过程中不会出现从0000110转移到0001011,而这正是比暴力求解法节约时间的地方。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stack>
using namespace std;
#define N 15
typedef struct
{
char name[101];
int deadline;
int cost;
}Course;
typedef struct
{
int timeUsed; //到此状态总共花费的时间
int minPoint; //此状态最小的被罚分数
int preState; //此状态对应的前一状态
int lastCourse; //从上一状态转移到此状态完成的course
}State;
Course homework[16];
State states[1<<N];
int n; //课程数
void outputResult()
{
stack<int> s;
int tempState;
int tempCourse;
char courseName;
tempState = (1<<n) - 1;
printf("%d\n", states[tempState].minPoint);
while(tempState != 0)
{
tempCourse = states[tempState].lastCourse;
s.push(tempCourse);
tempState = states[tempState].preState;
}
while(!s.empty())
{
tempCourse = s.top();
s.pop();
printf("%s\n", homework[tempCourse].name);
}
}
void processDP()
{
int i, j, maxStateN;
int temp;
int reduce;
int preState;
states[0].lastCourse = 0;
states[0].minPoint = 0;
states[0].preState = -1;
states[0].timeUsed = 0;
maxStateN = 1<<n;
for(i=1; i<maxStateN; i++)
{
states[i].minPoint = 0x7fffffff;
for(j=n-1; j>=0; j--) //这里j从n-1递减到0,是为了保证当罚分一样时,顺序按照字母顺序递增的方式排列
{
temp = 1<<j;
if(i & temp) //状态i中包含第j门课
{
preState = i - temp;
reduce = states[preState].timeUsed+homework[j].cost-homework[j].deadline;
if(reduce < 0)
{
reduce = 0;
}
if(states[i].minPoint > states[preState].minPoint + reduce)
{
states[i].minPoint = states[preState].minPoint + reduce;
states[i].lastCourse = j;
states[i].preState = preState;
states[i].timeUsed = states[preState].timeUsed + homework[j].cost;
}
}
}
}
}
int main()
{
int i;
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(i=0; i<n; i++)
{
scanf("%s%d%d", homework[i].name, &homework[i].deadline, &homework[i].cost);
}
processDP();
outputResult();
}
return 0;
}