很明显的状态压缩题目。当然了如果他的意识只有是求最小值没有要求输出那个输出的顺序,那么直接用背包,甚至是贪心都是可以做出来的,但是要求顺序这个背包的话没有办法吧数据记录下来。所以用二进制压缩。
所谓的二进制压缩就是用二进制来表示事情的完成程度和状况,比如1,就是0000001表示第一个作业做了,别的都没做,101表示1,3做了,别的没做。。。一直到2^n-1也即是11111111111111111.。。。。表示所有完成而n<=15不是很大完全可以做出来。思想和背包一样就是我们对于任何一个数1<=m<n那么作业i(i>.=0)如果我们选择了作业i那么作业i就应该做了,也就是相应的位置上m的二进制数是一个1,那么m&2^j就是一个大于0的数字,这样我们就可以用m-2^j来推导m的状态。比如现在m=1110001101而j=3,那么2^3=1000所以同或以后就是1110000101,也就是在1110000101的基础上选择j这个东西,遍历一遍所有的可以的j值就可以找到最优的情况。这样到最后111111111111111111111.。。。。就可以全部求出。
最后输出顺序是倒置的所以用递归,栈,哪怕是数组(倒置输出)也是可以的。这个不是重点就不多讲。
#include<iostream> using namespace std; #include<stdio.h> #include<cstring> #include<algorithm> #include<cstdlib> const int INF=99999999;//卡的比较严的话还是用0x7fffffff(int最大)比较好 const int Max=(1<<15)+10;相当于2^15+10;不过速度更快 struct Hom { char name[105]; int dead; int time;///要花费的时间。 }Dp[20]; struct hom { int score;///标记每一个阶段扣分最少的情况 int pre;标记在他之前的一个作业 int time;标记最完作业最少的时间 }dp[Max]; void output(int x) { if(!x)return ; output(x-(1<<dp[x].pre)); cout<<Dp[dp[x].pre].name<<endl; } int main() { int T,N; int i,j,k; cin>>T; while(T--) { cin>>N; memset(dp,0,sizeof(dp)); memset(Dp,0,sizeof(Dp)); for(i=0;i<N;i++) scanf("%s%d%d",&Dp[i].name,&Dp[i].dead,&Dp[i].time); int temp=1<<N; for(i=1;i<temp;i++) { dp[i].score=INF; for(j=N-1;j>=0;j--) { int M=1<<j; if(M&i) { int reduce=dp[i-M].time+Dp[j].time-Dp[j].dead;///Dp表示最原始的科目的数据。 if(reduce<0)reduce=0; if(reduce+dp[i-M].score<dp[i].score) { //cout<<"hao"<<endl; dp[i].score=reduce+dp[i-M].score; dp[i].time=dp[i-M].time+Dp[j].time; dp[i].pre=j; } } } } cout<<dp[temp-1].score<<endl; output(temp-1); } return 0; }