【状态DP】 HDU 1074 Doing Homework

原题直通车: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\n",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\n", ans[--d]);
    }
    return 0;
}


 

 

posted @ 2013-09-10 19:23  pangbangb  阅读(163)  评论(0编辑  收藏  举报