HDU/HDOJ 1074 Doing Homework
题目出处:http://acm.hdu.edu.cn/showproblem.php?pid=1074
dp状态压缩入门题,大牛无视。解法dp+状态压缩。参考hh大牛的这个图,http://www.cppblog.com/notonlysuccess/archive/2009/02/18/74168.aspx便于理解。题目的数据规模是15,本来采用了枚举和深搜的方法竟然都超时了。按理说不该吧。对状态压缩一无所知,参考了大牛们得解题报告,基本上是一样画葫芦写出来的。
对[1,1<<n)枚举每个值,当成是一个状态,保存到达这个状态也就是完成这个状态的作业被扣掉的最小学分。比如7的二进制值是111就代表完成了第一第二第三个作业三个作业被扣掉的最小学分。初值置为INF,然后对这个状态,比如i,包含的每个作业,如果存在没有完成这个作业的某个状态被扣的学分+在这个状态完成并被扣学分最小的情况下完成这个作业被扣的学分比i状态下被扣学分少,就更新。比如i=7,设被扣学分score[7]=min(score[3]+完成3后完成4被扣的学分,score[6]+完成6后完成1被扣的学分,score[5]+完成5后完成2被扣掉的学分)。
题目输入是按字典序输入,不用再排序。但是在更新时应注意这一点,具体注释里面有详细说明。
#include <iostream> #include <cstdio> #include <cstdlib> #include <ctime> #include <map> #include <cstring> #include <string.h> #include <fstream> #include <vector> #include <cmath> #include <algorithm> using namespace std; const int INF = 1<<29; const int N = 15; struct work{ string name; //课程名字 int deadline; //截止时间 int use_time; //需要时间 }; work data[N+1]; struct state{ int used_time; //已经使用时间 int reduce_score; //减掉的最小学分 int last_work; //此状态下前一个工作 int ith_work; //这个状态完成的最后一个工作 }; state d[1<<N]; int main(){ int tot, n, i, j, max_value; scanf("%d", &tot); while (tot--) { scanf("%d", &n); for (i=0; i<n; i++){ cin>>data[i].name; scanf("%d%d", &data[i].deadline, &data[i].use_time); } max_value = (int)(1<<n); int temp, score, last, reduce; d[0].reduce_score = 0; d[0].used_time = 0; d[0].last_work = -1; for (i=1; i<max_value; i++){ d[i].reduce_score = INF; /* 为什么j要从后往前枚举: 比如i的二进制=11,则应考虑1、完成01后最小花费+10的花费 2、完成10后最小花费+01的花费 两个值较小的那一个,如果相等当然是取1,其他情况类推 这和题目要求的字符串升序排列有关,与最小值无关 */ for (j=n-1; j>=0; j--){ temp = (int)(1<<j); //如果状态i中完成了第j个作业 if (i & temp){ //如果i==temp即只完成j则last=0; last = i-temp; reduce = d[last].used_time+data[j].use_time-data[j].deadline; reduce = reduce>0?reduce:0; if (d[last].reduce_score+reduce < d[i].reduce_score) { d[i].reduce_score = d[last].reduce_score+reduce; d[i].last_work = last; d[i].ith_work = j; d[i].used_time = d[last].used_time+data[j].use_time; } } } } printf("%d\n", d[max_value-1].reduce_score); int pos[N+1], k=0, x=max_value-1; //递推得出序列 while (x > 0){ pos[k++] = d[x].ith_work; x = d[x].last_work; } for (i=k-1; i>=0; i--) cout<<data[pos[i]].name<<endl; } return 0; }