POJ 15288 课程大作业 【状压dp】【字典序输出方案】【北大ACM/ICPC竞赛训练】

 其实这个转移方程比较简单,每次枚举最后一个完成的作业就行了。

转移方程是 dp[i] = min{ dp[ i - (1<<(k-1))  ] + score(i,k) }

score(i,k)是完成i这么多作业中最后一个完成的作业是k时,k带来的最小扣分值。

难点在于字典序输出方案,我建了个ans数组ans[i]代表i状态下最好的完成的最后一个作业。要注意处理字典序的时候,要把从i到0的一整条方案都找到,【因为题目要求的就是如果有最佳方案,输出字典序最小的,而不是要第i个完成的课程字典序较小】,不能光比当前course的字典序。

 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 #define INF 1000000000
 5 using namespace std;
 6 
 7 string course[20];
 8 int deadline[20],c[20],n;
 9 int dp[66000];//dp[i]代表做完【i状态代表的这么多作业】最少扣分 
10     //dp[0] = 0
11 int ans[66000];
12 vector<string> path;
13 
14 int finishday(int num){//N代价 
15     int an=0;
16     for(int i=1;i<=n;i++){//枚举这种作业完成情况下有没有完成i 
17         if( num & (1<<(i-1) ) ) an+=c[i]; 
18     }
19     return an;
20 }
21 
22 int score(int num,int k){//完成num状态这么多作业,最后一个完成的是k作业,k带来的扣分 
23     int s = 0;
24     s = deadline[k] - finishday( num - (1<<(k-1)) ) - c[k];//要扣s分 
25     if(s>0) s=0;//不用扣分
26     return -s; 
27 }
28 
29 int main(){
30     int t; cin>>t;
31     while(t--){
32         for(int i=0;i<66000;i++) dp[i]=INF;
33         cin>>n;
34         for(int i=1;i<=n;i++) cin>>course[i]>>deadline[i]>>c[i];
35                 
36         dp[0]=0;
37         for(int i=1;i<= (1<<n)-1;i++){//枚举所有的作业完成状态
38             int best;
39             for(int k=1;k<=n;k++){//枚举最后一个作业做的是什么
40                 if( i & (1<<(k-1) ) ) {
41                     int cnt = dp[ i - (1<<(k-1) ) ] + score(i,k);
42                     if( cnt<=dp[i] ){
43                         if( cnt < dp[i] ) best=k;
44                         else{  
45                             vector<int> path1,path2;//记录整条路径
46                             int num = i - (1<<(k-1));
47                             path1.push_back(k);
48                             while( ans[num]!=0 ){ int choose = ans[num]; path1.push_back(choose); num-=(1<<(choose-1)); }
49                             num = i - (1<<(best-1));
50                             path2.push_back(best);
51                             while( ans[num]!=0 ){ int choose = ans[num]; path2.push_back(choose); num-=(1<<(choose-1)); }
52                             
53                             reverse( path1.begin(),path1.end() );
54                             reverse( path2.begin(),path2.end() );
55                             
56                             if( path1<path2 ) best=k; 
57                         }//选字典序大的 
58                         dp[i]=cnt;
59                     }
60                 }
61             }
62             ans[i]=best;//这种情况下最后一个完成的作业应该是best
63             //cout<<i<<" "<<best<<endl;
64         }
65         
66         int num = (1<<n) -1; cout<<dp[num]<<endl;
67         while( ans[num]!=0 ){
68             int k = ans[num];
69             path.push_back( course[k] );
70             num-=(1<<(k-1));
71         }
72         for(int i=path.size()-1;i>=0;i--) cout<<path[i]<<endl;
73         path.clear();
74     }
75     
76     return 0;
77 }
原文地址:https://www.cnblogs.com/ZhenghangHu/p/9369998.html