HDU1074解题报告
HDU1074,如果仅仅从动态规划角度来看,它属于比较简单的DP问题,但是需要把问题进行转化。利用二进制构造出不同的状态,0代表课程在状态中,1表示课程不在状态中,初态全为0,终态全为1,终态的最小值即为所求结果。
假设输入n门课程,则共有2^n个状态,对于每个状态V({i})(集合{i}表示状态V所完成的课程)需要测试是否包含所有课程,如果包含的课程(j),则根据V({i}-j)计算V({i}),此过程中使用简单的DP公式。由此可见算法复杂度为O(n*2^n),属于NP问题,题目中说明输入数据n<=15,在这种小数量级的情况下,问题还是可解的。
题目中要求除了要输出扣掉的最小分数,还有输出课程完成的顺序。为了得到课程次序,记录下V({i})的前一个节点V({j}),{i}-{j}表示从V({j})->V({i})所添加的课程。详细代码如下:
#include <stdio.h> #include <stdlib.h> struct node{ int cost;//到达节点所需要的时间 int lowReduce;//到达节点减少的最小分数 int pre;//前一个节点的编号 }; struct course{ int deadine; int cost; char name[100]; }courses[16]; struct node *state; void output(int index){ int temp,id; //printf("index is %d\n",index); if(index<=0){ return; } output(state[index].pre);//输出前一个节点 temp=state[index].pre^index; id=0;//表示课程号,temp化成二进制,从右边数第一个1的index temp>>=1; while(temp>0){ id++; temp>>=1; //printf("id is %d\n",id); } //本节点要输出的课程名字 即(本节点所有课程名字减去前一个节点所有课程名字) printf("%s\n",courses[id].name); } int main(){ int n,m,i,j; int reduce,cur; int stateTotal; scanf("%d",&n); while(n--){ scanf("%d",&m); state=(struct node*)malloc(sizeof(struct node)*(1<<m)); for(i=0;i<m;i++){ scanf("%s%d%d",&courses[i].name,&courses[i].deadine,&courses[i].cost); } state[0].cost=0;state[0].lowReduce=0;state[0].pre=-1; stateTotal=1<<m; for(i=1;i<stateTotal;i++){ state[i].cost=0;state[i].lowReduce=0x7ffffff;state[i].pre=-1;//初始化 for(j=0;j<m;j++){ cur=1<<j;//i 从右边数第j位 if(i&cur){//state[i]中包括课程j state[i].cost=state[i^cur].cost+courses[j].cost;//i^cur即{i}-j reduce=state[i].cost-courses[j].deadine; if(reduce<0) reduce=0; if(state[i].lowReduce>=reduce+state[i^cur].lowReduce){ state[i].lowReduce=reduce+state[i^cur].lowReduce; state[i].pre=i^cur;//保存上一节点的编号 } } } } printf("%d\n",state[stateTotal-1].lowReduce);//输出最小花费 output(stateTotal-1); free(state); } return 0; }
程序思想比较简单,但是对二进制操作要求较高,难以理解的地方程序中已有详细注释。