二进制 状态压缩DP。
题目大意: 有n份作业需要做,每一份作业都有 一个截至时间和一个耗时(完成该作业需要的时间),每份作业每逾期一天就扣掉一分,要求完成所有作业并扣掉最少的学分,
并把完成作业的顺序输出出来!
很早之前就写过这道题,当时以为是道水题,直接贪心求解,一直WA,后来找到了一个反例,发现贪心真的不行,就放那了。这两天学了下状态压缩DP,把这道题给搞出来了。
思路:二进制状态压缩,1表示该作业已经完成,0表示未完成,而n最大15,也就是说最多只有2^15种状态。
把这2^n种状态遍历一遍就可以了,s表示其中一个状态,ans表示s的二进制有多少个1, 所以s可以由ans种方式到达,在这ans种方式中找一个最优的就是s的最优解;
看代码比较容易明白:(代码有一点我感觉很好,就是记录路径,我原来想的实用dfs来记录,发现不太容易实现,在网上查看了下别人的代码,恍然大悟,他们巧妙的运用了邻接表的思想,
佩服佩服!!)
View Code
1 # include<stdio.h>
2 # include<string.h>
3 # define INF 0xfffffff
4 int n,Min;
5 struct node{
6 char name[105];
7 int dead,cost;
8 }str[17];
9 int s[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
10 struct node1{
11 int score;
12 int day;
13 int pre,num;
14 }DP[33000];
15 int st[17];
16 int main()
17 {
18 int i,ncase,j,k,ans,reduce;
19 scanf("%d",&ncase);
20 while(ncase--)
21 {
22 scanf("%d",&n);
23 for(i=0;i<n;i++)
24 scanf("%s%d%d",str[i].name,&str[i].dead,&str[i].cost);
25 DP[0].day=0;
26 DP[0].score=0;
27 for(i=1;i<s[n];i++)
28 {
29 DP[i].score=INF;
30 for(j=n-1;j>=0;j--)//想一下为什么要从大到小?
31 {
32 if(i&s[j])
33 {
34 ans=i-s[j];
35 reduce=DP[ans].day+str[j].cost-str[j].dead;
36 if(reduce<0) reduce=0;
37 if(DP[ans].score+reduce<DP[i].score)
38 {
39 DP[i].score=DP[ans].score+reduce;
40 DP[i].day=DP[ans].day+str[j].cost;
41 DP[i].num=j;
42 DP[i].pre=ans;
43 }
44 }
45 }
46 }
47 printf("%d\n",DP[s[n]-1].score);
48 reduce=s[n]-1;
49 k=0;
50 while(reduce)
51 {
52 st[k++]=DP[reduce].num;
53 reduce=DP[reduce].pre;
54 }
55 for(i=k-1;i>=0;i--)
56 printf("%s\n",str[st[i]].name);
57 }
58 return 0;
59 }