【状态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;

}


 

 

你可能感兴趣的:(home)