据说是状态压缩DP,我用bfs()做了,其实思想是一样的,下面是我的代码:仅供参考。
1 //此题可用二进制表示所有的状态,一个进制数的每一位对应一种作业,如果是1表示做这种作业,
2 //但是还有个先做哪种作业的顺序,此时可以先让每一位都为1为第一种状态,到下一种状态时,
3 //可以把一个0变为1,表示下来做这种作业,如果遇到过这种状态,但先后顺序不一样,则保留最优解,
4 //可以举个例子,比如有三种作业,那么最初的状态为{(001) (010) (100)} 分别表示先做标记为1的作业,
5 //则下一个状态便为: (001) -> { (011) , (101) } (010) -> {(110) , (011)}; (100) ->{ (101), (110) }
6 //有相同的状态则会记录最优的一个状态,超时最少的,下来就到最后一种状态(111), (111)状态先取最优的即为结果
7 #include <iostream>
8 #include <cstring>
9 #include <queue>
10 #include <string>
11 using namespace std;
12 const int N = 1<<16;
13 //visit标记状态有没有访问过
14 //a记录超过的最短时间 b记录写作业一共花费的时间
15 //pre记录作业的先后顺序,回溯输出结果
16 bool visit[N]; int a[N], b[N], pre[N];
17 struct Node{
18 string sub; //科目
19 int ed, t;//结束时间、持续时间
20 }Sub[16];
21
22 void f(int p){//回溯输出结果
23 if(pre[p] == -1) return;
24 else{
25 int t = pre[p]; t = p ^ (1<<t);
26 f(t);
27 cout<<Sub[pre[p]].sub<<endl;
28 }
29 }
30 int bfs(int n){
31 memset(visit, false, sizeof(visit));
32 memset(a, 0, sizeof(a));
33 memset(pre, 0, sizeof(pre));
34 queue<int> q;
35 int i, j, t1, t2;
36 pre[0] = -1;
37 for(i = 0; i < n; ++i){ //初始化每种作业为初状态
38 j = 1 << i;
39 q.push(j); pre[j] = i; b[j] = Sub[i].t;
40 if(Sub[i].t <= Sub[i].ed) a[j] = 0;
41 else a[j] = Sub[i].t - Sub[i].ed;
42 visit[j] = true;
43 }
44 while(!q.empty()){
45 t1 = q.front(); q.pop();
46 for(i = 0; i < n; ++i){
47 j = 1 << i; t2 = t1 & j; //判断此作业是否做过,做过为1,未做过为0
48 if( !t2 ){
49 t2 = t1 | j;//下一种状态
50 if(!visit[t2]){ //没有访问过此状态
51 visit[t2] = true; b[t2] = b[t1] + Sub[i].t;
52 if(b[t2] > Sub[i].ed) a[t2] = a[t1] + b[t2] - Sub[i].ed;
53 else a[t2] = a[t1];
54 q.push(t2); pre[t2] = i;
55 }else{
56 int s1 = b[t1] + Sub[i].t;
57 if(s1 > Sub[i].ed) s1 = a[t1] + s1 - Sub[i].ed;
58 else s1 = a[t1];
59 if(a[t2] > s1){
60 a[t2] = s1; b[t2] = b[t1] + Sub[i].t;
61 q.push(t2); pre[t2] = i;
62 }
63 }
64 }
65 }
66 } cout<<a[t1]<<endl; f(t1);
67 }
68
69 int main(){
70 int t, N, i;
71 cin>>t;
72 while(t--){
73 cin>>N;
74 for(i = 0; i < N; ++i)
75 cin>>Sub[i].sub>>Sub[i].ed>>Sub[i].t;
76 bfs(N);
77 }
78 return 0;
79 }
2 //但是还有个先做哪种作业的顺序,此时可以先让每一位都为1为第一种状态,到下一种状态时,
3 //可以把一个0变为1,表示下来做这种作业,如果遇到过这种状态,但先后顺序不一样,则保留最优解,
4 //可以举个例子,比如有三种作业,那么最初的状态为{(001) (010) (100)} 分别表示先做标记为1的作业,
5 //则下一个状态便为: (001) -> { (011) , (101) } (010) -> {(110) , (011)}; (100) ->{ (101), (110) }
6 //有相同的状态则会记录最优的一个状态,超时最少的,下来就到最后一种状态(111), (111)状态先取最优的即为结果
7 #include <iostream>
8 #include <cstring>
9 #include <queue>
10 #include <string>
11 using namespace std;
12 const int N = 1<<16;
13 //visit标记状态有没有访问过
14 //a记录超过的最短时间 b记录写作业一共花费的时间
15 //pre记录作业的先后顺序,回溯输出结果
16 bool visit[N]; int a[N], b[N], pre[N];
17 struct Node{
18 string sub; //科目
19 int ed, t;//结束时间、持续时间
20 }Sub[16];
21
22 void f(int p){//回溯输出结果
23 if(pre[p] == -1) return;
24 else{
25 int t = pre[p]; t = p ^ (1<<t);
26 f(t);
27 cout<<Sub[pre[p]].sub<<endl;
28 }
29 }
30 int bfs(int n){
31 memset(visit, false, sizeof(visit));
32 memset(a, 0, sizeof(a));
33 memset(pre, 0, sizeof(pre));
34 queue<int> q;
35 int i, j, t1, t2;
36 pre[0] = -1;
37 for(i = 0; i < n; ++i){ //初始化每种作业为初状态
38 j = 1 << i;
39 q.push(j); pre[j] = i; b[j] = Sub[i].t;
40 if(Sub[i].t <= Sub[i].ed) a[j] = 0;
41 else a[j] = Sub[i].t - Sub[i].ed;
42 visit[j] = true;
43 }
44 while(!q.empty()){
45 t1 = q.front(); q.pop();
46 for(i = 0; i < n; ++i){
47 j = 1 << i; t2 = t1 & j; //判断此作业是否做过,做过为1,未做过为0
48 if( !t2 ){
49 t2 = t1 | j;//下一种状态
50 if(!visit[t2]){ //没有访问过此状态
51 visit[t2] = true; b[t2] = b[t1] + Sub[i].t;
52 if(b[t2] > Sub[i].ed) a[t2] = a[t1] + b[t2] - Sub[i].ed;
53 else a[t2] = a[t1];
54 q.push(t2); pre[t2] = i;
55 }else{
56 int s1 = b[t1] + Sub[i].t;
57 if(s1 > Sub[i].ed) s1 = a[t1] + s1 - Sub[i].ed;
58 else s1 = a[t1];
59 if(a[t2] > s1){
60 a[t2] = s1; b[t2] = b[t1] + Sub[i].t;
61 q.push(t2); pre[t2] = i;
62 }
63 }
64 }
65 }
66 } cout<<a[t1]<<endl; f(t1);
67 }
68
69 int main(){
70 int t, N, i;
71 cin>>t;
72 while(t--){
73 cin>>N;
74 for(i = 0; i < N; ++i)
75 cin>>Sub[i].sub>>Sub[i].ed>>Sub[i].t;
76 bfs(N);
77 }
78 return 0;
79 }