hdoj1074--Doing Homework (DP 状态压缩)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1074

思路:

看着数据很小,15,但是完成的顺序有15!情况,这么大的数据是无法实现的。上网查才知道要用状态压缩,用二进制表示状态,比如n=3时:111表示3門全部完成,011表示完成第1,2門的状态,000表示一門都没完成的情况。这样压缩之后最多就只有1<<15种状态了,然后直接dp就可以了。大体思路是从1状态到(1<<n-)1依次遍历,寻找上一个状态,使得到达此状态时总扣分最小,寻找上一个状态时j从n-1开始到0倒着遍历,这样才能保证最终结果是字典序最小的,这里要仔细想一下,比如寻找111的上一个状态,若j=1和j=0对应的上一个状态同时使111这个状态的总扣分最小,因为输入的顺序即按字典序来的,所以j=1对应的作业字典序要大于j=0的,所以要让j=0对应的作业先于j=1的完成,所以应该选择j=1作为上一个状态,故应该倒着遍历。详见代码。

AC代码如下:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int inf=0x3f3f3f3f;
 5 struct course{
 6     char name[105];
 7     int d,c;
 8 }a[20];
 9 
10 struct node{  //上一个状态,扣分值,总用时,该状态所做的作业编号
11     int pre,redu,cost,cou;
12 }dp[1<<15+5];
13 
14 int T,n,maxn;
15 
16 void print(int p){   //递归逆向输出
17     if(p){
18         print(dp[p].pre);
19         printf("%s\n",a[dp[p].cou].name);
20     }
21 }
22 
23 int main(){
24     scanf("%d",&T);
25     while(T--){
26         scanf("%d",&n);
27         maxn=1<<n;    
28         for(int i=0;i<n;i++)
29             scanf("%s%d%d",a[i].name,&a[i].d,&a[i].c);
30         memset(dp,0,sizeof(dp));
31         for(int i=1;i<maxn;i++){
32             dp[i].redu=inf;    //找最小的扣分,所以初始化为inf
33             for(int j=n-1;j>=0;j--){   //倒着遍历,为了结果字典序最小
34                 int t=1<<j;
35                 if(i&t){
36                     int tmp=i-t;    //上一个状态
37                     int tt=dp[tmp].cost+a[j].c-a[j].d;
38                     if(tt<0) tt=0;  //扣分值若小于0则置0
39                     if(dp[tmp].redu+tt<dp[i].redu){
40                         dp[i].redu=dp[tmp].redu+tt;
41                         dp[i].pre=tmp;
42                         dp[i].cost=dp[tmp].cost+a[j].c;
43                         dp[i].cou=j;
44                     }
45                 }
46             }
47         }
48         printf("%d\n",dp[maxn-1].redu);
49         print(maxn-1);
50     }
51     return 0;
52 }

猜你喜欢

转载自www.cnblogs.com/FrankChen831X/p/10389580.html