原题直通车:HDU 1074 Doing Homework
题意:有n门功课需要完成,每一门功课都有时间期限t、完成需要的时间d,如果完成的时间走出时间限制,就会被减
(d-t)个学分。问:按怎样的顺序做才能使得学分减得最少。
分析:因为n<=15数据比较小,可以用状态DP做。状态k(若k&(1<<j)==1表示第j门功课已经完成,反之未完成),
状态数最多为(1<<16)-1,每个状态k可由状太r(r<k, r&(1<<j)==0且k&(1<<j)==1, 其中0<j<n )得到。
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> using namespace std; const int maxn=1<<17; const int inf=0xFFFFFFF; struct node{ char s[105]; int t; //限制时间 int d; //完成时间 void read(){ scanf("%s%d%d",s,&t,&d); } }g[20]; struct DP{ int w; //扣的总学分 int sd; //总完成时间 int next, mj; }dp[maxn]; char ans[20][105]; int main(){ int T; scanf("%d",&T); while(T--){ int n; scanf("%d",&n); for(int i=0;i<n;++i) g[i].read(); dp[0].next=-1; dp[0].w=0; dp[0].sd=dp[0].st=0; int m=1<<(n+1); for(int i=1;i<m;++i){ dp[i].w=inf; for(int j=n-1;j>=0;--j) if((i>>j)&1){ int k=i-(1<<j); int c=dp[k].w; if(g[j].d+dp[k].sd>g[j].t) c+=g[j].d+dp[k].sd-g[j].t; if(dp[i].w>c){ dp[i].w=c; dp[i].next=k; dp[i].mj=j; dp[i].sd=dp[k].sd+g[j].d; } } } int u=0; for(int i=0;i<n;++i) u|=(1<<i); printf("%d ",dp[u].w); int d=0; while(true){ strcpy(ans[d++],g[dp[u].mj].s); u=dp[u].next; if(u==0) break; } while(d) printf("%s ", ans[--d]); } return 0; }